Spring Boot Part 2(OAuth 2.0)에서 카카오, 네이버 로그인 – 코드 구현

1. 개요

1부에서는 OAuth 2.0의 간단한 개념을 알아보고 네이버, 카카오 앱 등록을 완료했습니다.

마지막으로 Part 2에서는 API를 만들고 코드를 직접 구현합니다.

샘플 코드를 볼 때 다음 사항에 유의하십시오.

  • 스프링 시큐리티를 사용하지 마세요
  • ID와 PW를 인증하는 단순 로그인 코드를 작성하지 마세요.
  • 구성원을 의미 Member 테이블에 저장된 데이터는 각 개별 디자인에 남겨두고 여기서는 기본 데이터만 사용합니다.
  • 클라이언트 개발자(웹, 앱)와의 작업은 OAuth 2.0에 필수적이지만 여기에는 백엔드 코드만 작성됩니다.
    • 클라이언트 없이 테스트 소개

2. 서버의 역할

서버에서 구현해야 하는 세 가지 주요 논리가 있습니다.

  1. 카카오/네이버 등 OAuth 플랫폼 인증 후 프로필 정보 받기
  2. 이메일 정보로 사용자 확인(없으면 새로 로그인 수행)
  3. 액세스 토큰을 만든 후 공유

3. 개발 환경

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.0.3'
    id 'io.spring.dependency-management' version '1.1.0'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    runtimeOnly 'com.h2database:h2'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'

    implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
    implementation 'io.jsonwebtoken:jjwt-impl:0.11.5'
    implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5'
}

tasks.named('test') {
    useJUnitPlatform()
}

프로젝트 환경은 다음과 같습니다.

  • 점프보트 3.0.3
  • 자바 17
  • 스프링 웹
  • 스프링 데이터 JPA
  • H2 데이터베이스
  • 롬복
  • JWT 관련 라이브러리

4. 프리셋

4.1. Application.yml

jwt:
  secret-key: Z29nby10bS1zZXJ2ZXItZGxyamVvYW9yb3JodG9kZ290c3Atam9vbmdhbmduaW0teWVvbHNpbWhpaGFsZ2VveW8K

oauth:
  kakao:
    client-id: 160cd4f66fc928d2b279d78999d6d018
    url:
      auth: https://kauth.kakao.com
      api: https://kapi.kakao.com
  naver:
    secret: W_2DmcLfYU
    client-id: Y2i4SlApP7A1KZsUoott
    url:
      auth: https://nid.naver.com
      api: https://openapi.naver.com

JWT 토큰 생성을 위한 비밀키와 OAuth 요청을 위한 각종 정보를 입력합니다.

시크릿 값의 경우 외부에 노출되지 않도록 Vault와 같은 안전한 저장소에 저장할 수도 있습니다.

각 클라이언트는 등록된 클라이언트 ID를 사용해야 합니다. (포스팅 작성 후 앱 지울 예정)

4.2. 건설

@Configuration
public class ClientConfig {

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

RestTemplate을 사용하기 위해 Spring Bean 컴포넌트로 등록하기 위한 설정을 추가합니다.

5. 회원 도메인 정의

public enum OAuthProvider {
    KAKAO, NAVER
}

멤버의 로그인 유형을 저장하는 enum 클래스입니다.

“OAuth 인증 공급자”에서와 같이 OAuthProvider 네이밍을 사용했습니다.

@Getter
@Entity
@NoArgsConstructor
public class Member {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String email;

    private String nickname;

    private OAuthProvider oAuthProvider;

    @Builder
    public Member(String email, String nickname, OAuthProvider oAuthProvider) {
        this.email = email;
        this.nickname = nickname;
        this.oAuthProvider = oAuthProvider;
    }
}

회원 정보와 함께 Member 엔터티입니다.

이메일, 닉네임 또는 인증 유형과 같은 프로필 정보가 포함됩니다.

프로젝트의 성격에 따라 구성원(Member) 도메인 및 인증(Authentication) 경우에 따라 도메인이 분리되어 있어 이 부분은 디자인에 따라 변경될 수 있습니다.

6. 외부 API 요청

외부 API 요청을 위한 클라이언트 클래스를 생성해 보겠습니다.

API 요청 RestTemplate 그러나 선호도에 따라 다른 것을 사용할 수 있습니다.

  • OAuthApiClient: (인터페이스) 카카오나 네이버 API 요청 후 응답값을 반환하는 인터페이스
  • OAuthLoginParams: 카카오, 네이버 요청에 필요한 데이터를 담은 파라미터(인터페이스)
  • KakaoTokens, NaverTokens: 인증 API 응답
  • OAuthInfoResponse: 회원정보 API 응답(인터페이스)
  • RequestOAuthInfoService: 외부 API 요청의 중복 로직을 통합하는 클래스

인터페이스를 많이 사용했는데 다음과 같은 장점이 있습니다.

  • 카카오, 네이버 외의 OAuth 로그인 방식을 추가해도 쉽게 추가할 수 있습니다.
  • “외부 액세스 토큰 요청 -> 프로필 정보 요청 -> 이메일 및 닉네임 가져오기” 일반 논리를 하나로 결합할 수 있습니다.

6.1. OAuthLoginParams

public interface OAuthLoginParams {
    OAuthProvider oAuthProvider();
    MultiValueMap<String, String> makeBody();
}

OAuth 요청에 대한 매개변수 값이 있는 인터페이스입니다.

이 인터페이스의 구현은 @RequestBody 로도 사용되기 때문에 getXXX 이름을 사용해서는 안 됩니다.

@Getter
@NoArgsConstructor
public class KakaoLoginParams implements OAuthLoginParams {
    private String authorizationCode;

    @Override
    public OAuthProvider oAuthProvider() {
        return OAuthProvider.KAKAO;
    }

    @Override
    public MultiValueMap<String, String> makeBody() {
        MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
        body.add("code", authorizationCode);
        return body;
    }
}

카카오 API 요청 시 필요 authorizationCode 가지고 있는 수업입니다.

@Getter
@NoArgsConstructor
public class NaverLoginParams implements OAuthLoginParams {
    private String authorizationCode;
    private String state;

    @Override
    public OAuthProvider oAuthProvider() {
        return OAuthProvider.NAVER;
    }

    @Override
    public MultiValueMap<String, String> makeBody() {
        MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
        body.add("code", authorizationCode);
        body.add("state", state);
        return body;
    }
}

네이버는 authorizationCode 그리고 state 값이 필요합니다.

6.2. 카카오토큰, 네이버토큰

인증 코드를 기반으로 다른 플랫폼에서 액세스 토큰을 받기 위한 응답 모델입니다.

여러 값을 가져오지만 여기서 사용해야 하는 유일한 부분은 액세스 토큰입니다.

@Getter
@NoArgsConstructor
public class KakaoTokens {

    @JsonProperty("access_token")
    private String accessToken;

    @JsonProperty("token_type")
    private String tokenType;

    @JsonProperty("refresh_token")
    private String refreshToken;

    @JsonProperty("expires_in")
    private String expiresIn;

    @JsonProperty("refresh_token_expires_in")
    private String refreshTokenExpiresIn;

    @JsonProperty("scope")
    private String scope;
}

https://kauth.kakao.com/oauth/token 쿼리 결과입니다.

카카오 디벨로퍼 – 카카오 로그인 토큰 받기 의 응답 값 부분을 참조하십시오.

@Getter
@NoArgsConstructor
public class NaverTokens {

    @JsonProperty("access_token")
    private String accessToken;

    @JsonProperty("refresh_token")
    private String refreshToken;

    @JsonProperty("token_type")
    private String tokenType;

    @JsonProperty("expires_in")
    private String expiresIn;
}

https://nid.naver.com/oauth2.0/token 쿼리 결과입니다.

네이버 개발자 – 로그인 API 사양에 대한 액세스 토큰 발급 요청 응답 값을 참조하십시오.

6.3. OAuthInfoResponse

public interface OAuthInfoResponse {
    String getEmail();
    String getNickname();
    OAuthProvider getOAuthProvider();
}

Access Token으로 요청한 외부 API 프로필의 응답 값을 당사 서비스 모델로 변환하기 위한 인터페이스입니다.

카카오나 네이버의 이메일과 닉네임 정보가 필요하기 때문에 getter 방식을 추가했습니다.

@Getter
@JsonIgnoreProperties(ignoreUnknown = true)
public class KakaoInfoResponse implements OAuthInfoResponse {

    @JsonProperty("kakao_account")
    private KakaoAccount kakaoAccount;

    @Getter
    @JsonIgnoreProperties(ignoreUnknown = true)
    static class KakaoAccount {
        private KakaoProfile profile;
        private String email;
    }

    @Getter
    @JsonIgnoreProperties(ignoreUnknown = true)
    static class KakaoProfile {
        private String nickname;
    }

    @Override
    public String getEmail() {
        return kakaoAccount.email;
    }

    @Override
    public String getNickname() {
        return kakaoAccount.profile.nickname;
    }

    @Override
    public OAuthProvider getOAuthProvider() {
        return OAuthProvider.KAKAO;
    }
}

https://kapi.kakao.com/v2/user/me 쿼리 결과입니다.

코코아 개발자 – 사용자 정보 얻기 참조하여 생성된 응답값입니다.

원래는 더 많은 응답 값이 있지만 필요한 데이터만 걸러내기 위한 것입니다. @JsonIgnoreProperties(ignoreUnknown = true) 사용되었습니다.

@Getter
@JsonIgnoreProperties(ignoreUnknown = true)
public class NaverInfoResponse implements OAuthInfoResponse {

    @JsonProperty("response")
    private Response response;

    @Getter
    @JsonIgnoreProperties(ignoreUnknown = true)
    static class Response {
        private String email;
        private String nickname;
    }

    @Override
    public String getEmail() {
        return response.email;
    }

    @Override
    public String getNickname() {
        return response.nickname;
    }

    @Override
    public OAuthProvider getOAuthProvider() {
        return OAuthProvider.NAVER;
    }
}

https://openapi.naver.com/v1/nid/me 쿼리 결과입니다.

네이버 개발자 – 네이버 회원 프로필 검색을 위한 API 사양 참조하여 생성된 응답값입니다.

또한 @JsonIgnoreProperties(ignoreUnknown = true) 불필요한 값 없이 원하는 값만 가져옵니다.

6.4. OAuthApiClient

public interface OAuthApiClient {
    OAuthProvider oAuthProvider();
    String requestAccessToken(OAuthLoginParams params);
    OAuthInfoResponse requestOauthInfo(String accessToken);
}

OAuth 요청에 대한 클라이언트 클래스입니다.

  • oAuthProvider(): 클라이언트 유형을 반환합니다.
  • requestAccessToken: Authorization Code 기반의 인증 API를 요청하여 Access Token 획득
  • requestOauthInfo: 접근 토큰 기반으로 이메일, 닉네임 등 프로필 정보 수집

@Component
@RequiredArgsConstructor
public class KakaoApiClient implements OAuthApiClient {

    private static final String GRANT_TYPE = "authorization_code";

    @Value("${oauth.kakao.url.auth}")
    private String authUrl;

    @Value("${oauth.kakao.url.api}")
    private String apiUrl;

    @Value("${oauth.kakao.client-id}")
    private String clientId;

    private final RestTemplate restTemplate;

    @Override
    public OAuthProvider oAuthProvider() {
        return OAuthProvider.KAKAO;
    }

    @Override
    public String requestAccessToken(OAuthLoginParams params) {
        String url = authUrl + "/oauth/token";

        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

        MultiValueMap<String, String> body = params.makeBody();
        body.add("grant_type", GRANT_TYPE);
        body.add("client_id", clientId);

        HttpEntity<?> request = new HttpEntity<>(body, httpHeaders);

        KakaoTokens response = restTemplate.postForObject(url, request, KakaoTokens.class);

        assert response != null;
        return response.getAccessToken();
    }

    @Override
    public OAuthInfoResponse requestOauthInfo(String accessToken) {
        String url = apiUrl + "/v2/user/me";

        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        httpHeaders.set("Authorization", "Bearer " + accessToken);

        MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
        body.add("property_keys", "(\"kakao_account.email\", \"kakao_account.profile\")");

        HttpEntity<?> request = new HttpEntity<>(body, httpHeaders);

        return restTemplate.postForObject(url, request, KakaoInfoResponse.class);
    }
}

코코아 개발자 카카오 로그인 토큰 받기 그리고 사용자 정보 검색 참조.

RestTemplate 외부 요구 사항에 따라 사용에 의해 사전 정의됨 KakaoTokens, KakaoInfoResponse 로 응답 값을 가져옵니다.

@Component
@RequiredArgsConstructor
public class NaverApiClient implements OAuthApiClient {

    private static final String GRANT_TYPE = "authorization_code";

    @Value("${oauth.naver.url.auth}")
    private String authUrl;

    @Value("${oauth.naver.url.api}")
    private String apiUrl;

    @Value("${oauth.naver.client-id}")
    private String clientId;

    @Value("${oauth.naver.secret}")
    private String clientSecret;

    private final RestTemplate restTemplate;

    @Override
    public OAuthProvider oAuthProvider() {
        return OAuthProvider.NAVER;
    }

    @Override
    public String requestAccessToken(OAuthLoginParams params) {
        String url = authUrl + "/oauth2.0/token";

        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

        MultiValueMap<String, String> body = params.makeBody();
        body.add("grant_type", GRANT_TYPE);
        body.add("client_id", clientId);
        body.add("client_secret", clientSecret);

        HttpEntity<?> request = new HttpEntity<>(body, httpHeaders);

        NaverTokens response = restTemplate.postForObject(url, request, NaverTokens.class);

        assert response != null;
        return response.getAccessToken();
    }

    @Override
    public OAuthInfoResponse requestOauthInfo(String accessToken) {
        String url = apiUrl + "/v1/nid/me";

        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        httpHeaders.set("Authorization", "Bearer " + accessToken);

        MultiValueMap<String, String> body = new LinkedMultiValueMap<>();

        HttpEntity<?> request = new HttpEntity<>(body, httpHeaders);

        return restTemplate.postForObject(url, request, NaverInfoResponse.class);
    }
}

네이버 개발자들이 로그인 API 사양 그리고 회원 프로필 조회 API 사양 참조.

RestTemplate 외부 요구 사항에 따라 사용에 의해 사전 정의됨 NaverTokens, NaverInfoResponse 로 응답 값을 가져옵니다.

6.5. RequestOAuthInfoService

@Component
public class RequestOAuthInfoService {
    private final Map<OAuthProvider, OAuthApiClient> clients;

    public RequestOAuthInfoService(List<OAuthApiClient> clients) {
        this.clients = clients.stream().collect(
                Collectors.toUnmodifiableMap(OAuthApiClient::oAuthProvider, Function.identity())
        );
    }

    public OAuthInfoResponse request(OAuthLoginParams params) {
        OAuthApiClient client = clients.get(params.oAuthProvider());
        String accessToken = client.requestAccessToken(params);
        return client.requestOauthInfo(accessToken);
    }
}

지금까지 완료 OAuthApiClient 서비스 클래스인 .

KakaoApiClient, NaverApiClient 직접 주입하여 사용하면 중복되는 코드가 많을 것입니다. List<OAuthApiClient> 붙여넣기해서 지도만들면 그냥 쓰셔도 됩니다.

참고 List<인터페이스> 삽입되면 인터페이스의 모든 구현이 목록에 배치됩니다.

7. JWT(액세스 토큰)를 생성합니다.

네이버와 카카오 인증이 완료되면 클라이언트에게 엑세스 토큰을 전달해야 합니다.

여기서 Access Token은 네이버나 카카오의 토큰이 아닌 내 서비스의 인증 토큰입니다.

클라이언트에게 OAuth 플랫폼 접근 토큰을 부여할 때 플랫폼별 유효기간 관리가 번거롭고 도난 가능성도 없다.

JWT 관련 부분은 본 글의 주제가 아니므로 자세한 설명은 생략한다.

7.1. JwtTokenProvider

@Component
public class JwtTokenProvider {

    private final Key key;

    public JwtTokenProvider(@Value("${jwt.secret-key}") String secretKey) {
        byte() keyBytes = Decoders.BASE64.decode(secretKey);
        this.key = Keys.hmacShaKeyFor(keyBytes);
    }

    public String generate(String subject, Date expiredAt) {
        return Jwts.builder()
                .setSubject(subject)
                .setExpiration(expiredAt)
                .signWith(key, SignatureAlgorithm.HS512)
                .compact();
    }

    public String extractSubject(String accessToken) {
        Claims claims = parseClaims(accessToken);
        return claims.getSubject();
    }

    private Claims parseClaims(String accessToken) {
        try {
            return Jwts.parserBuilder()
                    .setSigningKey(key)
                    .build()
                    .parseClaimsJws(accessToken)
                    .getBody();
        } catch (ExpiredJwtException e) {
            return e.getClaims();
        }
    }
}

JWT 토큰을 생성하는 헬퍼 클래스입니다.

7.2. AuthTokens

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class AuthTokens {
    private String accessToken;
    private String refreshToken;
    private String grantType;
    private Long expiresIn;

    public static AuthTokens of(String accessToken, String refreshToken, String grantType, Long expiresIn) {
        return new AuthTokens(accessToken, refreshToken, grantType, expiresIn);
    }
}

사용자에게 전달된 서비스의 인증 토큰 값입니다.

7.3. AuthTokensGenerator

@Component
@RequiredArgsConstructor
public class AuthTokensGenerator {
    private static final String BEARER_TYPE = "Bearer";
    private static final long ACCESS_TOKEN_EXPIRE_TIME = 1000 * 60 * 30;            // 30분
    private static final long REFRESH_TOKEN_EXPIRE_TIME = 1000 * 60 * 60 * 24 * 7;  // 7일

    private final JwtTokenProvider jwtTokenProvider;

    public AuthTokens generate(Long memberId) {
        long now = (new Date()).getTime();
        Date accessTokenExpiredAt = new Date(now + ACCESS_TOKEN_EXPIRE_TIME);
        Date refreshTokenExpiredAt = new Date(now + REFRESH_TOKEN_EXPIRE_TIME);

        String subject = memberId.toString();
        String accessToken = jwtTokenProvider.generate(subject, accessTokenExpiredAt);
        String refreshToken = jwtTokenProvider.generate(subject, refreshTokenExpiredAt);

        return AuthTokens.of(accessToken, refreshToken, BEARER_TYPE, ACCESS_TOKEN_EXPIRE_TIME / 1000L);
    }

    public Long extractMemberId(String accessToken) {
        return Long.valueOf(jwtTokenProvider.extractSubject(accessToken));
    }
}

AuthTokens .

  • generate: MemberId(사용자 식별값) 취득 및 엑세스 토큰 생성
  • extractMemberId: Access Token에서 MemberId(사용자 식별값) 추출

8. 책임, 서비스

이제 지금까지 만든 코드를 사용하여 최종 비즈니스 로직을 빌드해 보겠습니다.

8.1. OAuthLoginService

@Service
@RequiredArgsConstructor
public class OAuthLoginService {
    private final MemberRepository memberRepository;
    private final AuthTokensGenerator authTokensGenerator;
    private final RequestOAuthInfoService requestOAuthInfoService;

    public AuthTokens login(OAuthLoginParams params) {
        OAuthInfoResponse oAuthInfoResponse = requestOAuthInfoService.request(params);
        Long memberId = findOrCreateMember(oAuthInfoResponse);
        return authTokensGenerator.generate(memberId);
    }

    private Long findOrCreateMember(OAuthInfoResponse oAuthInfoResponse) {
        return memberRepository.findByEmail(oAuthInfoResponse.getEmail())
                .map(Member::getId)
                .orElseGet(() -> newMember(oAuthInfoResponse));
    }

    private Long newMember(OAuthInfoResponse oAuthInfoResponse) {
        Member member = Member.builder()
                .email(oAuthInfoResponse.getEmail())
                .nickname(oAuthInfoResponse.getNickname())
                .oAuthProvider(oAuthInfoResponse.getOAuthProvider())
                .build();

        return memberRepository.save(member).getId();
    }
}

처음에 설명한대로 논리를 정확하게 작성했습니다.

  1. 카카오/네이버 등 OAuth 플랫폼 인증 후 프로필 정보 받기
  2. 이메일 정보로 사용자 확인(없으면 새로 로그인 수행)
  3. 액세스 토큰을 만든 후 공유

맛을보기 위해 findOrCreateMember 별도의 부품 MemberService 로 구분하셔도 상관없습니다.

코드에서 볼 수 있듯이 이는 네이버와 카카오에 국한된 것이 아니라 공통적인 로직이며 인터페이스만 사용했습니다.

대신에 login 메서드를 호출할 때 KakaoLoginParams, NaverLoginParams 둘 중 어느 것이 들어오느냐에 따라 API 요청을 하는 시점이 달라진다.

새로운 Google 또는 Facebook 로그인이 추가되더라도 이 코드는 변경할 필요가 없으므로 추가해도 안전합니다.

8.2. 인증 컨트롤러

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/auth")
public class AuthController {
    private final OAuthLoginService oAuthLoginService;

    @PostMapping("/kakao")
    public ResponseEntity<AuthTokens> loginKakao(@RequestBody KakaoLoginParams params) {
        return ResponseEntity.ok(oAuthLoginService.login(params));
    }

    @PostMapping("/naver")
    public ResponseEntity<AuthTokens> loginNaver(@RequestBody NaverLoginParams params) {
        return ResponseEntity.ok(oAuthLoginService.login(params));
    }
}

사용자의 요청을 받는 컨트롤러 부분입니다.

특별한 부분이 없으며 구현을 매개 변수로 직접 가져옵니다. login 유일한 차이점은 전화를 거는 것입니다.

9. 클래스 다이어그램

위에서 작성한 코드의 간단한 표현은 다음과 같습니다.

10. API 요청 테스트

클라이언트 없이 서버에서 테스트해보자.

10.1. OAuth 로그인

네이버와 카카오 로그인 페이지를 직접 만들고 접속 후 로그인하세요.

1장에서 다루었으므로 자세한 설명은 생략한다.

로그인 후 리디렉션 URI로 전달된 인증 코드를 확인합니다.

  • 코코아: https://kauth.kakao.com/oauth/authorize?client_id=160cd4f66fc928d2b279d78999d6d018&redirect_uri=http://localhost:8080/kakao/callback&response_type=code
  • 네이버: https://nid.naver.com/oauth2.0/authorize?response_type=code&client_id=Y2i4SlApP7A1KZsUoott&state=hLiDdL2uhPtsftcU&redirect_uri=http://localhost:8080/naver/callback

10.2. API 호출

자신의 API 호출 방법으로 API를 직접 호출하십시오. B. Postman, Curl 또는 IntelliJ IDE.

Talend API Tester라는 Chrome 확장 프로그램을 사용했습니다.

응답 값으로 액세스 토큰을 받으면 성공한 것입니다.

10.2.1. 코코아 로그인 호출

코코아는 authorizationCode 매개변수를 추가해야만 호출됩니다.

10.2.2. 네이버 로그인 콜

네이버는 authorizationCode 게다가 state 값도 수신되어 전달됩니다.

10.3. 회원정보 문의

요청된 데이터가 올바르게 입력되었는지 확인하기 위해 간단한 회원 API를 빌드합니다.

10.4. 액세스 토큰 확인

액세스 토큰 확인은 두 가지 방법으로 수행할 수 있습니다.

  1. API 생성 및 호출
  2. 테스트 코드에서 코드 빌드 AuthTokensGenerator.extractMemberId 메서드를 직접 실행

편리한 방법으로 확인하실 수 있습니다.

위에서 얻은 Access Token으로 올바른 MemberId를 얻으면 성공한 것입니다.

졸업 증서

지금까지 Spring Boot는 OAuth 2.0을 사용하여 인증 기능을 개발했습니다.

OAuth 2.0에 대해 잘 몰라서 많이 어렵고 난감했는데 막상 구현해보니 쉽게 해결되었습니다.

참조