Skip to content

Commit

Permalink
feat : 한 API로 여러 SNS(카카오, 구글) 로그인할 수 있도록 기능 구현
Browse files Browse the repository at this point in the history
  • Loading branch information
wjdwnsdnjs13 committed Aug 19, 2024
1 parent d8bccaa commit 6180525
Show file tree
Hide file tree
Showing 3 changed files with 191 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package org.depromeet.spot.application.member.controller;

import jakarta.validation.Valid;

import org.depromeet.spot.application.common.jwt.JwtTokenUtil;
import org.depromeet.spot.application.member.dto.request.RegisterV2Req;
import org.depromeet.spot.application.member.dto.response.JwtTokenResponse;
import org.depromeet.spot.domain.member.Member;
import org.depromeet.spot.domain.member.enums.SnsProvider;
import org.depromeet.spot.usecase.port.in.oauth.OauthUsecase;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@RestController
@RequiredArgsConstructor
@Slf4j
@Tag(name = "(v2) Oauth")
public class OauthController {

private final OauthUsecase oauthUsecase;

private final JwtTokenUtil jwtTokenUtil;

@PostMapping("/api/v2/members")
@ResponseStatus(HttpStatus.CREATED)
@Operation(summary = "Member 회원가입 API")
public JwtTokenResponse create(@RequestBody @Valid RegisterV2Req request) {

Member member = request.toDomain();
Member memberResult = oauthUsecase.create(request.accessToken(), member);

return new JwtTokenResponse(jwtTokenUtil.getJWTToken(memberResult));
}

@GetMapping("/api/v2/members/{snsProvider}/{accessToken}")
@ResponseStatus(HttpStatus.OK)
@Operation(summary = "Member 로그인 API")
public JwtTokenResponse login(
@PathVariable("snsProvider")
@Parameter(name = "snsProvider", description = "KAKAO/GOOGLE", required = true)
SnsProvider snsProvider,
@PathVariable("accessToken")
@Parameter(
name = "accessToken",
description = "sns accessToken",
required = true)
String accessToken) {

Member member = oauthUsecase.login(snsProvider, accessToken);
return new JwtTokenResponse(jwtTokenUtil.getJWTToken(member));
}

// TODO : /api/v2/members를 RequestMapping으로 빼면 구글 로그인에서 4xx Exception 발생
@GetMapping("/api/v2/members/authorization/{snsProvider}")
@ResponseStatus(HttpStatus.OK)
@Operation(summary = "(백엔드용)accessToken을 받아오기 위한 API")
public String getAccessToken2(
@PathVariable("snsProvider") SnsProvider snsProvider, @RequestParam String code) {
String token = oauthUsecase.getOauthAccessToken(snsProvider, code);
log.info("snsProvider : {}", snsProvider);
log.info("token : \n{}", token);
return token;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package org.depromeet.spot.usecase.port.in.oauth;

import org.depromeet.spot.domain.member.Member;
import org.depromeet.spot.domain.member.enums.SnsProvider;
import org.depromeet.spot.domain.team.BaseballTeam;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;

public interface OauthUsecase {

Member create(String accessToken, Member member);

Member login(SnsProvider snsProvider, String accessToken);

String getOauthAccessToken(SnsProvider snsProvider, String authorizationCode);

@Getter
@Builder
@AllArgsConstructor
class MemberInfo {
private final String nickname;
private final String profileImageUrl;
private final int level;
private final String levelTitle;
private final String mascotImageUrl;
private String teamImageUrl;
private final Long teamId;
private final String teamName;
private final Long reviewCntToLevelUp;

public static MemberInfo of(Member member, Long reviewCntToLevelUp) {
return MemberInfo.builder()
.nickname(member.getNickname())
.profileImageUrl(member.getProfileImage())
.level(member.getLevel().getValue())
.levelTitle(member.getLevel().getTitle())
.mascotImageUrl(member.getLevel().getMascotImageUrl())
.teamImageUrl(null)
.teamId(null)
.teamName(null)
.reviewCntToLevelUp(reviewCntToLevelUp)
.build();
}

public static MemberInfo of(
Member member, BaseballTeam baseballTeam, Long reviewCntToLevelUp) {
return MemberInfo.builder()
.nickname(member.getNickname())
.profileImageUrl(member.getProfileImage())
.level(member.getLevel().getValue())
.levelTitle(member.getLevel().getTitle())
.mascotImageUrl(member.getLevel().getMascotImageUrl())
.teamImageUrl(baseballTeam.getLogo())
.teamId(baseballTeam.getId())
.teamName(baseballTeam.getName())
.reviewCntToLevelUp(reviewCntToLevelUp)
.build();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package org.depromeet.spot.usecase.service.oauth;

import org.depromeet.spot.common.exception.member.MemberException.InactiveMemberException;
import org.depromeet.spot.common.exception.member.MemberException.MemberNicknameConflictException;
import org.depromeet.spot.domain.member.Level;
import org.depromeet.spot.domain.member.Member;
import org.depromeet.spot.domain.member.enums.SnsProvider;
import org.depromeet.spot.usecase.port.in.member.level.ReadLevelUsecase;
import org.depromeet.spot.usecase.port.in.oauth.OauthUsecase;
import org.depromeet.spot.usecase.port.out.member.MemberRepository;
import org.depromeet.spot.usecase.port.out.oauth.OauthRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import lombok.RequiredArgsConstructor;

@Service
@Transactional
@RequiredArgsConstructor
public class OauthService implements OauthUsecase {

private final OauthRepository oauthRepository;
private final MemberRepository memberRepository;
private final ReadLevelUsecase readLevelUsecase;

@Override
public Member create(String accessToken, Member member) {
if (memberRepository.existsByNickname(member.getNickname())) {
throw new MemberNicknameConflictException();
}
Member memberResult = oauthRepository.getOauthRegisterUserInfo(accessToken, member);
Level initialLevel = readLevelUsecase.findInitialLevel();

return memberRepository.save(memberResult, initialLevel);
}

@Override
public Member login(SnsProvider snsProvider, String accessToken) {
Member memberResult = oauthRepository.getOauthLoginUserInfo(snsProvider, accessToken);
Member existedMember = memberRepository.findByIdToken(memberResult.getIdToken());

// 회원 탈퇴 유저일 경우 재가입
if (existedMember.getDeletedAt() != null) {
throw new InactiveMemberException();
}
return existedMember;
}

@Override
public String getOauthAccessToken(SnsProvider snsProvider, String authorizationCode) {
return oauthRepository.getOauthAccessToken(snsProvider, authorizationCode);
}
}

0 comments on commit 6180525

Please sign in to comment.