회원가입 서비스 로직 기록 (NestJS)

bin-lee·2022년 1월 7일
0

회원가입 서비스를 만들 때 service단이 좀 헷갈려서 정리할 겸 포스팅을 작성하려고 한다. 뭘 import하고 뭘 설치하는지 항상 헷갈려서 기록하고자.. 그래도 포스팅을 하면 확실히 정리가 잘되는 것 같다. 아마도.. 😇


클라이언트에서 받은 body 데이터를 DTO 처리하고 service단으로 보냈다.

@Injectable()
export class CatsService {
  signUp(body: CatRequestDto) {
  }
}

이제 body로 받은 데이터를 유효성 검사와 패스워드 암호화를 걸쳐 DB에 저장하는 과정을 거쳐야 한다. DB에 저장하려면 쿼리를 써야 하므로 스키마를 서비스단 안에서 사용하기 위해 DI를 해 주자.

@Injectable()
export class CatsService {
  constructor(@InjectModel(Cat.name) private readonly catModel: Model<Cat>) {}
  
  signUp(body: CatRequestDto) {
  }
}

constructor(@InjectModel(Cat.name) private readonly catModel: Model<Cat>) {}가 스키마를 사용하기 위해 DI 해 준 코드이다. 그런데 그냥 이렇게만 하면 사용할 수 없다. cat.module에 가서 아래처럼 import를 해 줘야 한다.

@Module({
  imports: [MongooseModule.forFeature([{ name: Cat.name, schema: CatSchema }])],
})

이제 Cat모델을 서비스단 안에서 사용 가능하다.


🍡 signUp 로직

body에 email, name, password 데이터가 들어오면 유효성 검사, 패스워드 암호화를 거쳐 DB에 저장해야 한다.

(1) 유효성 검사

const isCatExist = await this.catModel.exists({ email });
이메일은 중복되면 안 되므로 중복 검사를 한다. exists 쿼리 메서드는 DB의 email 필드를 검색하고 해당 이메일과 일치하는 값이 존재하는지 검사한 후 Promise<boolean>으로 return 한다. 그리고 이 return 값이 true라면 조건문을 통해 아래와 같은 오류 코드를 발생시킨다.

  if (isCatExist) {
    throw new UnauthorizedException('해당 메일이 이미 존재합니다.'); 
    // 403 에러를 발생시켜주는 자동화된 클래스
    // throw new HttpException('해당 메일이 이미 존재합니다.', 403);와 동일
  }
    }

(2) 비밀번호 암호화

비밀번호 암호화를 위한 bycript 라이브러리가 있다. 암호화, 즉 hash를 해 주는 라이브러리로 별도의 설치가 필요하다. npm으로 아래 두 개를 모두 설치해 준다.

npm i bcrypt
npm i -D @types/bcrypt

설치 후 import * as bcrypt from 'bcrypt' 해 주면 bcrypt를 사용할 수 있다! 이제 body에 있는 password를 bcrypt로 hashing하여 hashedPassword에 할당한다. 암호화된 비밀번호는 hashedPassword에 담겨 있다.

const hashedPassword = await bcrypt.hash(password, 10);

(3) 버추얼 필드

비밀번호 암호화는 성공했으나, 추후 DB에 저장하면 httpException 필터 형식에 따라 password의 암호화된 값이 노출되어 반환될 것이다.

{
"success": true,
"data": {
    "_id": "~~~"
    "name": "~~~"
    "password": 암호화된 값 노출!!
    ⁝
    ⁝
  }
}

이를 막기 위해 버추얼 필드를 만들어 주면 된다. 몽구스에서 제공하는 버추얼 필드는 패스워드 값을 숨길 수 있게 해 준다. 실제로 DB에 저장되는 필드는 아니고 비지니스 로직에서 사용할 수 있도록 제공해 주는 가상의 필드다. 이는 schema 단계에서 virtual('필드네임') 메소드를 사용하여 작성한다.

CatSchema.virtual('readOnlyData').get(function (this: Cat) {
  return {
    id: this.id,
    email: this.email,
    name: this.name,
  };
});

readOnlyData는 필드네임, this는 schema 클래스 Cat 그 자체인 Model이다. return되는 값들은 클라이언트한테 보여줄 데이터로, 패스워드는 노출될 필요가 없기 때문에 제외했다. 버추얼은 가상으로 필터링해서 나간다는 개념으로 이해하면 된다. 단순히 사용자가 보게되는 영역이며 DB에 존재하지 않는다. 때문에 schema class Cat 내부에 추가를 해 줄 때에도 readonly로, readonly readOnlyData 이렇게 추가한다.


(4) DB 저장

유효성 검사 때 catModelexists 메서드를 사용했던 것처럼 이번에는 catModelcreate 메서드를 사용한다. create 메서드를 이용하여 DB에 값을 저장할 수 있다. 회원가입 서비스에서 DB에 저장해야 하는 데이터는 email, name, 암호화된 hashedPassword이다. 값들을 object 형식으로 나타내면 아래와 같다.

const cat = await this.catModel.create({
  email,
  name,
  password: hashedPassword,
});

return cat.readOnlyData;

password가 들어 있지 않은 cat.readOnlyData 버추얼 필드를 return 해 주면 회원가입 서비스 로직 1차적으로 끝!


@Post()
  async signUp(@Body() body: CatRequestDto) {
    return await this.catModel.signUp(body); // 프로미스를 리턴해서 await

controller에서 미완성된 코드를 완성시키고 postman에서 실행하면 mongoDB Compass에 데이터가 들어가 있는 것을 확인할 수 있다.

profile
🚀 오늘 배운 건 오늘 적자

0개의 댓글