20230817(목)_ 최종프로젝트 진행
금일 달성 항목
1) 프로젝트 세팅
2) 회사 및 채용공고 API 작성 중
문제 해결 과정 1 - repository 이슈
[문제]
import { EntityRepository, Repository } from "typeorm";
import { User } from "./entity/user.entity";
@EntityRepository(User)
export class UserRepository extends Repository<User>{}
위와같이 레포지토리를 가져와서 사용할 수 없는 상태에서 어떻게 하면 더 나은 코드를 작성할 수 있는지 고민이 필요했습니다.
그래서 레포지토리 없이 서비스로만 가져오는 방법을 택했고 코드를 작성하고자 했습니다.
EntityMetadataNotFoundError: No metadata for "User" was found.
그런데 위와같이 찾을 수 없다는 오류가 떴고
서비스의 내용과 레포지토리의 중복된 내용을 한 화면에서 담으려면 어떻게해야할지 고민이었습니다.
[시도 및 해결]
튜터님께 여쭤보니 InjectRepository로 동일하게 서비스를 가져오면 된다고 하셨습니다.
@Injectable()
export class AuthService {
constructor(
@InjectRepository(User)
private userService: UserService,
private readonly authRepository: Repository<User>,
private jwtService: JwtService, //jwt 불러오기
) {}
전체코드는 아래와 같습니다
auth.service.ts
import {
HttpException,
HttpStatus,
Injectable,
UnauthorizedException,
} from '@nestjs/common';
import { UserService } from './user.service';
import { UserDTO } from './dto/user.dto';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { User } from './entity/user.entitiy';
import * as bcrypt from 'bcrypt';
import { Payload } from './security/payload.interface';
import { JwtService } from '@nestjs/jwt';
@Injectable()
export class AuthService {
constructor(
@InjectRepository(User)
private userService: UserService,
private readonly authRepository: Repository<User>,
private jwtService: JwtService, //jwt 불러오기
) {}
// 사용자 등록 요청이 왔을때
async registerUser(newUser: UserDTO): Promise<UserDTO> {
let userFind: UserDTO = await this.userService.findByFields({
where: { username: newUser.username },
});
// 만약 사용자를 찾았으면
if (userFind) {
throw new HttpException('Username aleady userd!', HttpStatus.BAD_REQUEST);
}
return await this.authRepository.save(newUser);
}
// 로그인
async validateUser(
userDTO: UserDTO,
): Promise<{ accessToken: string } | undefined> {
let userFind: UserDTO = await this.userService.findByFields({
where: { username: userDTO.username },
});
// bcrypt 를 비교할때
const valudatePassword = await bcrypt.compare(
userDTO.password,
userFind.password,
);
// 사용자 찾지 못했을때, 비밀번호가 틀렸을때
if (!userFind || !valudatePassword) {
throw new UnauthorizedException();
}
const payload: Payload = { id: userFind.id, username: userFind.username };
return { accessToken: this.jwtService.sign(payload) };
}
}
그리고 구조에 대해서 튜터님께 추가로 여쭤보았습니다.
nest.js에서는 이제 레포지토리를 제공하지 않지만 그럼에도 3계층으로 나누어서 사용해야하는 이유가 있을까요?
라는 질문에 튜터님께서는
리펙토링을 할때 기존에 코드가 생성된 영역에서 수정을 진행한다면 기능을 보완하는데 용이하다고 하셨습니다.
2계층으로 만든 뒤 추후에 필요에 의해 3계층으로 분리하게되면 더 많은 시간을 소요할 수 있기 때문입니다.
결국 미래에 고도화를 위한 세팅으로 3계층이 용이하므로 상황에따라 판단하여 작성하면 된다고 합니다.
[알게된 점]
지금 순간의 코드를 빨리 작성하는 것도 좋지만 미래에 고도화 또는 다른 개발자가 보았을때 직관적이고 간결해야한다는 점을 계속 인지해야겠다고 생각했습니다.
문제 해결 과정 2 - nest.js id type에 대한 의문점
[문제]
왜 엔티티와 모델에서의 id는 number로 가져오고 있는데
controller에서 타입을 설정할때 string으로 전환하고 +id를 통해 숫자로 전환하는 건지 의문이 생겼습니다.
company.entity.ts
@Entity()
export class Company {
@PrimaryGeneratedColumn()
id: number;
company.controller.ts
// 회사 1개 조회
@Get(':id')
finOne(@Param('id') id: string) {
return this.companyService.findOne(+id); //string 으로 가져와서 숫자로 변환
}
[시도 및 해결]
찾아보니
일반적으로 RESTful API에서 리소스의 ID는 문자열보다 숫자 형태인 경우가 많은데
Nest.js의 라우팅에서 동적 파라미터를 정의할 때, 기본적으로 문자열 타입으로 처리 된다고 합니다.
따라서 URL 경로에서 받아온 파라미터는 문자열로 취급되는 것이라 합니다.
그래서 위 코드 작성과 같이 파라미터가 문자열로 처리되는 상황에서 숫자 타입으로 일치시키기 위해 +id와 같이 형 변환을 하는 것이라 합니다. 해당 과정이 있기에 타입 불일치로 인한 오류를 방지할 수 있다고 합니다.
[알게된 점]
코드에는 다 이유가 있습니다.