프로젝트

트러블슈팅 카카오 소셜로그인

늘곰's 2023. 10. 13. 16:02

카카오 소셜 로그인을 구현하던중 트러블이 생겻다.

문제는 회원가입을 하지 않은 회원이 카카오로 로그인을 하게되면 

회원가입을 시켜줘야한다.

그런데 기존에 회원가입시에 만들어놓은 password나 confirmPassword 와 같은 정보들이 
존재하지 않아 에러가 뜨게 되었다. 계속해서 '비밀번호와 비밀번호 확인이 일치하지 않습니다. 라는 에러가 뜨게되었고 '
이 문제가 계속해서 해결되지 않았다.

 

에러\LocalMingle-BE\src\users\users.service.ts:59 throw new BadRequestException('비밀번호와 비밀번호 확인이 일치하지 않습니다.'); ^ BadRequestException: 비밀번호와 비밀번호 확인이 일치하지 않습니다.

 

 

users.service 부분 회원가입 

 // todo: after heedragon
  async create(createUserDto: CreateUserDto): Promise<User> {
  
    const { email, password, nickname, intro, confirmPassword, profileImg } = createUserDto;

    const user = await this.findByEmail({ email });
    if (user) {
      throw new ConflictException('이미 등록된 이메일입니다.');
    }
  // 리팩토링시 !== 로 변경
   if  (password != confirmPassword){
      throw new BadRequestException('비밀번호와 비밀번호 확인이 일치하지 않습니다.');
    }

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

   return this.prisma.user.create({
      data: {
        email,
        password: hashedPassword,
        UserDetail: {
          create: {
            nickname,
            intro,
            profileImg,
          },
        }
      },
    });
  }

 

auth.service 

기존에 이런식으로 소셜로그인코드를 구성하였는데 
참고한 블로그와 다르게 지금 프로젝트에선 createDto에 
이것보다 훨씬 많은 정보들이 들어가야 회원가입이 되는 구조였다.

그래서 createUser 쪽에서 필요한 데이터들이 없다고 에러가 계속 발생하고 있는 상황이었다.

  async OAuthLogin({ req, res }) {
    // 1. 회원조회
    let user = await this.usersService.findByEmail({ email: req.user.email }); //user를 찾아서

 	// 2, 회원가입이 안되어있다면? 자동회원가입
	if (!user) user = await this.usersService.create({ ...req.user });
        
      console.log('소셜로그인회원가입 : ', createUser); // createUser 정보를 콘솔에 출력
      user = await this.usersService.create(createUser);
    }

    // 3. 회원가입이 되어있다면? 로그인(AT, RT를 생성해서 브라우저에 전송)한다
    this.getAccessToken({ user, res }); // res를 전달
    this.setRefreshToken({ user, res }); // res를 전달
    //리다이렉션
    res.redirect('http://127.0.0.1:5500');
  }

1차 수정 auth.service

1차로 문제점을 찾은 뒤 지금 보고있는 부분처럼 우리 서비스에 필요한 부분(createDto)들을 전부받아오도록 수정하였다.
intro와 profileImg는 null 값도 허용되기때문에 undefind가 되었지만 문제는 없었다. 

이렇게하니 또 오류가 생겼다. 
우리 프로젝트는 createUser를 할때 비밀번호를 bcrypt 로 저장하도록 구성되어있는데 

지금 auth.service 에서는 그냥 비밀번호 대신  kakao id 값을 비밀번호 대신 쓰면서 그 id 이 해싱되지 않은 상태로 

계속해서 들어가고있어서 패스워드가 bcrypt  가 되지않았다는 오류를 계속해서 발생시켰다. 

  async OAuthLogin({ req, res }) {
    // 1. 회원조회
    let user = await this.usersService.findByEmail({ email: req.user.email }); //user를 찾아서

    if (!user) {
      // 이 부분에서 아이디 생성과 관련된 코드를 추가해야 합니다.
      const createUser = {
        email: req.user.email, // 사용자의 이메일을 사용하여 아이디 생성
        nickname: req.user.name,
        password: req.user.password, 
        confirmPassword: req.user.password, 
        intro: req.user.intro,
        profileImg: req.user.profileImg,
        // 다른 필드도 설정해야 할 수 있음
      };
      console.log('소셜로그인회원가입 : ', createUser); // createUser 정보를 콘솔에 출력
      user = await this.usersService.create(createUser);
    }

 

jwt-social-kakao.strategy

카카오 스트레티지는 이런식으로 구성되어있었는데 여기서 password가 그냥 id 값이 그대로 계속해서 

 auth.service 로 넘어가고있엇고  auth.service 에서는 카카오로 로그인한 사용자가 회원이 아니면 

회원가입을 시켜줘야하는데 회원가입로직에서 bcrypt 를 사용한 해싱된 비밀번호를 요구하고있기때문에 여기서 문제가 생겨서 에러가 계속 발생하였다.

import { PassportStrategy } from '@nestjs/passport';
import { Strategy } from 'passport-kakao';
import * as bcrypt from 'bcrypt';

export class JwtKakaoStrategy extends PassportStrategy(Strategy, 'kakao') {
  constructor() {
    super({
      clientID: process.env.KAKAO_CLIENT_ID,
      clientSecret: process.env.KAKAO_CLIENT_SECRET,
      callbackURL: process.env.KAKAO_CALLBACK_URL,
      scope: ['account_email', 'profile_nickname'],
    });
  }

  async validate(accessToken: string, refreshToken: string, profile: any) {
    console.log('accessToken:' + accessToken);
    console.log('refreshToken:' + refreshToken);
    console.log(profile);
    console.log(profile._json.kakao_account.email);


    return {
      name: profile.displayName,
      email: profile._json.kakao_account.email,
      password: profile.id
      confirmPassword: profile.id
    };
  }
}

 

문제해결 

카카오 스트레티지 안에 해싱을하는 bcrypt  패키지를 연결하고 여기서 해싱을 구현하는 코드를 추가하였다.

const hashedPassword = await bcrypt.hash(profile.id.toString(), 10); 이 코드로 기존에 비밀번호로 쓰던 프로필 id를 

해싱비밀번호로 변경하였고 

그것을 패스워드와 confirm에 넣어 해싱된 비밀번호를 만들어  auth.service에 전달해서 에러를 해결하였다.

import { PassportStrategy } from '@nestjs/passport';
import { Strategy } from 'passport-kakao';
import * as bcrypt from 'bcrypt';

export class JwtKakaoStrategy extends PassportStrategy(Strategy, 'kakao') {
  constructor() {
    super({
      clientID: process.env.KAKAO_CLIENT_ID,
      clientSecret: process.env.KAKAO_CLIENT_SECRET,
      callbackURL: process.env.KAKAO_CALLBACK_URL,
      scope: ['account_email', 'profile_nickname'],
    });
  }

  async validate(accessToken: string, refreshToken: string, profile: any) {
    console.log('accessToken:' + accessToken);
    console.log('refreshToken:' + refreshToken);
    console.log(profile);
    console.log(profile._json.kakao_account.email);

    const hashedPassword = await bcrypt.hash(profile.id.toString(), 10);

    return {
      name: profile.displayName,
      email: profile._json.kakao_account.email,
      password: hashedPassword,
      confirmPassword: hashedPassword,
    };
  }
}