애플 로그인을 추가해보자

Choi Wontak·2025년 4월 14일

아이쿠MSA

목록 보기
4/12
post-thumbnail

난이도 ⭐️
작성 날짜 2025.03.03

고민 내용

그동안 우리 앱은 카카오 로그인만 지원할 수 있도록 기획을 작성해왔고, 실제로 서버 쪽에선 그렇게 코드를 작성하였다.

그러나 그때까진 모르고 있었다.

iOS 앱은 가능하면 애플 로그인을 포함시켜야 한다는 것을…

🤔
지금 있는 카카오 로그인 로직을 재활용할 순 없을까?


찾아보기

iOS 파트의 팀원 분께서 말씀하시기 전까진 Apple 로그인 자체를 고려하고 있지 않았다. 방식만 늘어나봤자 팀원들의 구현 부담만 증가할 것이라고 생각했기 때문이다. 그러나 애플 심사에서 거절될 수 있다는 말씀을 듣고 추가해야만 한다는 것을 알게 되었다.

다행히 OIDC 방식은 표준화된 방식이었기 때문에 OIDC 로직에서 크게 바뀔 점은 없었다. (이 글에서도 딱히 다루지 않을 예정)

문제가 되는 부분은 엔티티 설계부터 '카카오 로그인'만 고려해 짰다는 것이다.

private Long kakaoId;

이 상황에서 구조를 바꿀 방법은 두 가지가 있었다.

  1. kakaoId는 그대로 두고, appleId 필드만 추가한다.
  2. 두 이름을 통합해 oauthId 필드로 변경하고, provider 필드를 추가한다.

1번은 가독성도 좋고, 기존 로직에서 크게 변화가 없다는 장점이 있지만.. 만약 로그인 방식이 하나 더 추가된다면..!
같은 코드를 중복해서 써 주어야 하고, 로그인 방식에 따른 분기도 더욱 복잡해질 것이라고 생각했다. 그래서 선택한 방법은 2번

2번에서의 문제점은 oauthId만으로는 해당 로그인 사용자가 이전에 가입한 이력이 있는지를 체크하기 어렵다는 것이다.

// 기존 코드
@PostMapping("/sign-in")
public BaseResponse<SignInTokenResDto> signIn(@RequestBody @Valid SignInDto signInDto){
        SignInTokenResDto signInTokenResDto = loginService.signIn(signInDto.getIdToken());
    return new BaseResponse<>(signInTokenResDto);
}

그래서 로그인 시 요청 방식을 둘로 나누어 처리하였다.

@PostMapping("/sign-in/kakao")
public BaseResponse<SignInTokenResDto> signInKakao(@RequestBody @Valid SignInDto signInDto){
    SignInTokenResDto signInTokenResDto = loginService.signInKakao(signInDto.getIdToken());

    return new BaseResponse<>(signInTokenResDto);
}

@PostMapping("/sign-in/apple")
public BaseResponse<SignInTokenResDto> signInApple(@RequestBody @Valid SignInDto signInDto){
    SignInTokenResDto signInTokenResDto = loginService.signInApple(signInDto.getIdToken());

    return new BaseResponse<>(signInTokenResDto);
}

로그인에서 성공하면, payload로 kakaoId를 가진 Access Token을 발급한다.
부가설명으로으로 이 방식을 선택한 최초의 이유를 설명하자면...

로그인 시 서버에서 멤버를 검증하고 회원을 저장하지 않은 채로 Access Token을 발급해야 했다. 회원가입 절차에서 멤버 정보가 함께 넘어오면 멤버를 DB에 저장하고 그제서야 memberId가 생성되기 때문이다.
회원가입 시 토큰이 필요 없다면, 무분별한 회원가입이 이루어져 문제가 생길 것이라고 판단하였다.

지금은 클라이언트에서 회원가입 시 kakaoId가 아닌 OIDC idToken을 보내고 있어 한 번 더 검증하면서 이러한 문제가 해결되었으니 더이상 AT가 kakaoId에 의존할 필요가 전혀 없다!

또한, 현재의 방식을 계속 이용한다면, kakaoId, appleId, ... 이런 식으로 토큰 발급 시 같은 로직의 복제가 무한히 일어날 것이다.

return Jwts.builder()
                .setSubject(authentication.getName()) // memberId
                .claim("auth", authorities)
                .setExpiration(accessTokenExpiresIn)
                .signWith(key, SignatureAlgorithm.HS256)
                .compact();

멤버id를 기준으로 토큰을 생성하고,

@Override
    public UserDetails loadUserByUsername(String memberId) throws UsernameNotFoundException {
        Member member = memberQueryRepository.findByMemberId(Long.parseLong(memberId))
                .orElseThrow(MemberNotFoundException::new);

        MDC.put("accessMemberId", String.valueOf(member.getId()));

        // Member Role을 리스트 형태로 전달
        return new MemberAdaptor(member, List.of(new SimpleGrantedAuthority(member.getRole().name())));
    }

토큰 인증 시에도 memberId를 통해 멤버를 찾을 수 있도록 변경하였다.


결론

기획에는 없었지만... 그래도 확장성 좀 고려해서 짤 걸 그랬다!!


profile
백엔드 주니어 주니어 개발자

0개의 댓글