250x250
Notice
Recent Posts
Recent Comments
Link
«   2025/07   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
Archives
Today
Total
관리 메뉴

devlog_owen

[TIL] nest.js에서 네이버 소셜로그인 기능구현하기 본문

TIL

[TIL] nest.js에서 네이버 소셜로그인 기능구현하기

developer_owen 2024. 1. 18. 11:19
728x90

설치 및 세팅

 

하단의 명령어로 passport-Naver를 다운받는다.

$ npm install passport-naver

 

https://developers.naver.com/apps/#/myapps/g4Sz3QoF8ZQFrEPBLmmq/config

 

애플리케이션 - NAVER Developers

 

developers.naver.com

 

여기서 적당히(?) API 설정을 하면 된다. 

 


naver-auth.guard.ts

import { AuthGuard } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';

@Injectable()
export class NaverAuthGuard extends AuthGuard('naver') {}

 

Nest.js에서 제공하는 passport 라이브러리를사용해 네이버 소셜로그인을 처리하는 커스텀 가드다. 

AuthGuard('naver')의 naver는 passport에서 사용되는 전략 이름을 나타낸다. 이런식으로 구현해두면 nest.js 안에서 자동적으로 naver 가드를 적용해서 전략파일을 거칠 수 있다.

 


naver.strategy.ts, naver-admin.strategy.ts

import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { Strategy } from 'passport-naver';
import { ConfigService } from '@nestjs/config';

@Injectable()
export class NaverStrategy extends PassportStrategy(Strategy, 'naver') {
  constructor(private configService: ConfigService) {
    super({
      clientID: configService.get<string>('NAVER_CLIENT_ID'),
      clientSecret: configService.get<string>('NAVER_CLIENT_SECRET'),
      callbackURL: configService.get<string>('NAVER_REDIRECT_URI'),
    });
  }
  async validate(
    accessToken: string,
    refreshToken: string,
    profile: any,
    done: any,
  ) {
    const userEmail = profile._json.email;
    const userNick = profile._json.nickname;
    const userProfileImage = profile._json.profile_image;
    const userProvider = profile.provider;
    const userProfile = {
      userEmail,
      userNick,
      userProvider,
      userProfileImage,
    };

    return { accessToken, userProfile };
  }
}

 

 

passportStrategy를 상속받아서 소셜로그인 전략을 구현한다.프로젝트에서는 유저와 사장이 나눠져서 두가지 전략파일을 만들고 콜백링크도 두가지로 나눴다.

 

 

NAVER_CLIENT_ID=''
NAVER_CLIENT_SECRET=''
NAVER_REDIRECT_URI='https://localhost:3000/auth/naver/callback'
NAVER_ADMIN_REDIRECT_URI='http://localhost:3000/auth/naver/admin/callback'

 


auth.controller.ts

@Controller('auth')
export class AuthController {
  constructor(private readonly authService: AuthService) {}
//네이버 일반사용자 소셜로그인
  @Get('naver')
  @UseGuards(AuthGuard('naver'))
  async naverLogin(): Promise<void> {}

  @Get('naver/callback')
  @UseGuards(AuthGuard('naver'))
  async naverLoginCallback(@Req() req, @Res() res) {
    return await this.authService.naverLoginCallback({ req, res });
  }

  //네이버 지점업주 소셜로그인
  @Get('naver-admin')
  @UseGuards(AuthGuard('naver-admin'))
  async naverAdminLogin(): Promise<void> {}

  @Get('naver/admin/callback')
  @UseGuards(AuthGuard('naver-admin'))
  async naverAdminLoginCallback(@Req() req, @Res() res) {
    return await this.authService.naverAdminLoginCallback({ req, res });
  }
  }

 

auth.service.ts

 

@Injectable()
export class AuthService {


//네이버 로그인
  async naverLoginCallback({ req, res }) {
    // 네이버 이메일로 사용자를 찾는다.
    const email = req.user.userProfile.userEmail;
    const nickname = req.user.userProfile.userNick;
    let OAuthUser = await this.userRepository.findOne({
      where: { email },
    });

    // 네이버 사용자의 이메일 값을 변수 지정
    // user의 아이디 값을 해시화 해서 변수 지정
    const hashedNaverPassword = await bcrypt.hash(email, 10);

    // 해당 이메일 사용자가 없다면 회원가입 로직처럼 회원 생성
    // 비밀번호는 user의 아이디 값을 해시화 해서 변수 지정한 값을 사용
    if (!OAuthUser) {
      OAuthUser = await this.userRepository.save({
        email,
        nickname,
        password: hashedNaverPassword,
      });
    }

    const accessToken = this.generateAccessToken(
      OAuthUser.id,
      OAuthUser.nickname,
    );
    const refreshToken = this.generateRefreshToken(OAuthUser.id);

    await this.userService.update(OAuthUser.id, {
      currentRefreshToken: refreshToken,
    });

    if (OAuthUser)
      res.redirect(
        `http://localhost:3000/login/success?accessToken=${accessToken}&refreshToken=${refreshToken}`, //받아주는 페이지 만들어야함
      );
    else res.redirect('http://localhost:3000/login/failure');
  }

  //네이버 지점업주 회원가입/로그인
  async naverAdminLoginCallback({ req, res }) {
    // 네이버 이메일로 사용자를 찾는다.
    const email = req.user.userProfile.userEmail;
    const nickname = req.user.userProfile.userNick;
    let OAuthUser = await this.userRepository.findOne({
      where: { email },
    });

    // 네이버 사용자의 이메일 값을 변수 지정
    // user의 아이디 값을 해시화 해서 변수 지정
    const hashedNaverPassword = await bcrypt.hash(email, 10);

    // 해당 이메일 사용자가 없다면 회원가입 로직처럼 회원 생성
    // 비밀번호는 user의 아이디 값을 해시화 해서 변수 지정한 값을 사용
    if (!OAuthUser) {
      OAuthUser = await this.userRepository.save({
        email,
        nickname,
        password: hashedNaverPassword,
        role: 1,
      });
    }

    const accessToken = this.generateAccessToken(
      OAuthUser.id,
      OAuthUser.nickname,
    );
    const refreshToken = this.generateRefreshToken(OAuthUser.id);

    await this.userService.update(OAuthUser.id, {
      currentRefreshToken: refreshToken,
    });

    if (OAuthUser)
      res.redirect(
        `http://localhost:3000/login/success?accessToken=${accessToken}&refreshToken=${refreshToken}`, //받아주는 페이지 만들어야함
      );
    else res.redirect('http://localhost:3000/login/failure');
  }
}

 

 

 

위에서 말했듯이 유저, 사장별로 컨트롤러를 다르게했다. 애초에 유저,사장 회원가입 컨트롤러가 따로 있기 때문이다.

일반유저의 경우는

1. localhost:3000/auth/naver에서 네이버에서 로그인하고

2. 다시 웹페이지로 돌아와서 이번에는 콜백 엔드포인트로가서 localhost:3000/auth/naver/callback에서 회원가입/로그인을 한 후 accesstoken과 refreshtoken을 준다.

 

admin으로 잘 들어옴!


 

728x90