[GDG]홍대 맛집 아카이빙 프로젝트 백엔드 개발 #4 - 오늘은 OAuth 끝내죠?

2025. 7. 17. 02:53·팀 프로젝트/[2025][GDG]홍대 맛집 아카이빙 프로젝트

인스타그램에 데일리스크럼을 남기는 중인데..

어제 오늘 할 일을 미리 적어놓았다.

 

JWT 코드 해석해야하고

Value 어노테이션이 인식되게 고치고

네이버 / 카카오 로그인 기능 만들고

추가로 RefreshToken 기능도 만들면 좋을듯

 

순서대로 할 예정

 

추가로 토큰방식의 로그인에 대해 써놓은 글이 있는데 참고하면 좋을듯

https://ocblog.tistory.com/56


1. JWT 코드 해석하기

JWT 토큰의 매커니즘은 대충..

로그인 완료 시 유저정보를 담아 JWT 토큰 발급 후 쿠키에 저장 (백)

-> 프론트에서 헤더에 JWT 토큰을 넣어 보냄 -> 백엔드가 헤더에 있는 JWT 토큰을 분석하여 회원전용기능의 경우에는 해당 유저의 요청을 거부할지 승인할지 결정

 

우리팀은 재학생/외부인만 구분하면 되기 때문에 JWT 토큰에 Roles : outer / inner 정도로만 넣으면 될 듯하다. 사실 여기까지는 어제 다 끝냄

 

이제 해야할 것은 프론트엔드에서 헤더에 JWT토큰을 담아 보내면 이를 해독하는게 필요하다.

고고


 

JWT 토큰을 해석하는 클래스(위)와 JWT 토큰의 만료여부를 확인하는 클래스 (아래)

토큰이 만료되었을 경우에는 401 코드를 반환하고 프론트에서 새 코드를 발급받도록 링크를 연결시켜주어야한다고 한다.

 

--

헤더와 페이로드 부분을 인코딩하고

헤더와 페이로드의 정보를 시크릿키를 이용한 해시알고리즘으로 해싱하여 인코딩한 것이 서명

--

 

대충 이정도면 된 것 같고.. 복잡한 코드를 하나하나 확인해보면

먼저 토큰 해석 클래스에서

가장 중요한게 parsClaimsJws(token) 인데, token은 우리가 전할 JWS 토큰을 인자로 주는거고, 인자로 준 토큰의 유효성을 검사하는 것이다. 나의 시크릿 키를 통해 서명부분을 검증한다.

 

GPT에게 물어보니 parsClaimsJws는 받은 토큰에서 서명을 분리하고 인코딩된 헤더.페이로드 부분을 해시 알고리즘을 통해 내가 준 시크릿 값으로 새로운 서명을 만든 뒤, 이를 기존의 것과 대조한다고 한다. 추가로 토큰 만료여부까지 계산한다고 한다.세상 참 편하다.

 

parsClaimsJws는 이 모든 과정을 끝낸 후 이상이 없으면 Jws<Claims> 객체를 반환하며, 여기에서 헤더, 페이로드, 서명에 접근할 수 있다. 만약 이상이 있을 경우 오류를 반환한다. 위의 코드는 try-catch문을 통하여 오류가 없으면 true, 있으면 false를 반환한다. 아마도 상황에 따라 다른 메시지를 반환해야 응답코드로 전해주기 쉬울 것 같은데..

 

다른 블로그를 보니까 enum을 사용하는데 아직 enum은 몰라서 그냥 바로 반환하려고 한다.

 

    //try - catch 구조 필요
    public String getSubject(String token) {
        return Jwts.parserBuilder().setSigningKey(secret).build().parseClaimsJws(token).getBody().getSubject();
    }

    public String validateToken(String token) {
        try {
            Jws<Claims> claims = Jwts.parserBuilder().setSigningKey(secret).build().parseClaimsJws(token);
            if (claims.getBody().getExpiration().before(new Date())) {
                return "성공";
            }
            return "성공";
        } catch (ExpiredJwtException e) {
            return "만료된 JWT";
        } catch (SecurityException | MalformedJwtException e) {
            return "잘못된 타입";
        } catch (UnsupportedJwtException e) {
            return "잘못된 JWT 구조";
        } catch (io.jsonwebtoken.security.SignatureException e) {
            return "서명실패(위조데이터)";
        }
    }

예외처리도 해주었다.

https://veneas.tistory.com/entry/Spring-Boot-JWT-JSON-Web-Token-%ED%86%A0%ED%81%B0-%EA%B8%B0%EB%B0%98-%EC%9D%B8%EC%A6%9D <- 이 블로그를 참조함

저기서 반환되는 메시지를 response하면 됨

 

그리고 filter를 만들어서 저기서 나오는 값들을 받아가면 됨

대략적인 구조가 프론트요청 -> 헤더에서 토큰 빼내기(필터) -> 검증하고 데이터 가져오기 (필터 -> provider 연결) -> ...

이런 구조로 하면 될 듯하다,.

 

package com.hongchelin.Service.JWT;

import io.jsonwebtoken.*;
import jakarta.annotation.PostConstruct;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import java.util.Base64;
import java.util.Date;
import java.util.List;

@Component
public class JWTProvider {
    //@Value("${jwt.secret}")
    private String secret = "mySuperSecretKeyForJwtTokenGeneration1234567890kjsdfgalkdsafsdfkjlafdsfgfkjagsdfkjhsdagfkhgsfhvakhjfgfayudfgiwuefyqfhvkjahfgiaodgfoiuguiafyhvsdakjhfbasdf";
    private Long validTokenTime = 1000L * 60 * 60 * 24 * 3;

    @PostConstruct
    protected void init() {
        secret = Base64.getEncoder().encodeToString(secret.getBytes());
        System.out.println("secret: " + secret);
    }

    public String createToken(String email, String roles) {
        Claims claims = Jwts.claims().setSubject(email);
        claims.put("role", roles);
        Date now = new Date();
        return Jwts.builder()
                .setClaims(claims)
                .setIssuedAt(now)
                .setExpiration(new Date(now.getTime() + validTokenTime))
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }

    //try - catch 구조 필요
    public Claims getSubject(String token) {
        Jws<Claims> jwsClaims = Jwts.parserBuilder()
                .setSigningKey(secret)
                .build()
                .parseClaimsJws(token);

        Claims claims = jwsClaims.getBody();

        return claims;
    }

    public String validateToken(String token) {
        try {
            Jws<Claims> claims = Jwts.parserBuilder().setSigningKey(secret).build().parseClaimsJws(token);
            if (claims.getBody().getExpiration().before(new Date())) {
                return "성공";
            }
            return "성공";
        } catch (ExpiredJwtException e) {
            return "만료된 JWT";
        } catch (SecurityException | MalformedJwtException e) {
            return "잘못된 타입";
        } catch (UnsupportedJwtException e) {
            return "잘못된 JWT 구조";
        } catch (io.jsonwebtoken.security.SignatureException e) {
            return "서명실패(위조데이터)";
        }
    }
}

만들면서 Provider도 약간 수정하고

/**
 * 필터는 헤더에서 토큰을 가져오고 이를 검증하기 위해 Provider에 데이터를 넘기는 역할을 한다.
 * 서비스계층에서는 filter에 JWT토큰을 넘기고 이의 메시지를 받아서 ResponseEntity를 반환함.
 *
 * getTokenFromHeader -> 헤더에 있는 AccessToken을 가져온다.
 */
import io.jsonwebtoken.Claims;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

@Component
public class JWTFilter implements JWTFilterInterface {
    private final JWTProvider jwtProvider;

    public JWTFilter(JWTProvider jwtProvider) {
        this.jwtProvider = jwtProvider;
    }

    @Override
    public ResponseEntity<?> getTokenFromHeader(String header, HttpServletRequest request) {
        String token = request.getHeader("AccessToken");

        if (token != null) {
            return checkValidity(token);
        } else {
            return ResponseEntity.status(HttpStatus.PRECONDITION_REQUIRED)
                    .body("Access token is required");
        }
    }

    @Override
    public ResponseEntity<?> checkValidity(String token) {
        String validityMessage = jwtProvider.validateToken(token);

        if (validityMessage.equals("성공")) {
            return getUserRoles(token);
        } else {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                    .body(validityMessage);
        }
    }

    @Override
    public ResponseEntity<?> getUserRoles(String token) {
        Claims claims = jwtProvider.getSubject(token);
        String userRole = (String) claims.get("roles");
        String userEmail = claims.getSubject();

        Map<String, Object> userInfo = new HashMap<>();
        userInfo.put("userRole", userRole);
        userInfo.put("userEmail", userEmail);

        return ResponseEntity.ok(userInfo);
    }
}

JWT 필터를 대충 만들었다..

 

처음에는 String 타입의 userRole(재학생 여부)만 반환하려다가 그냥 JSON으로 이메일(이나 추가될 데이터 등...)까지 JSON 형태로 보내는게 나을 것 같아서 ResponseEntity에 담아 보내도록 수정함. 확실히 와일드카드를 쓰니가 편하다.

HttpStatus statusCode = response.getStatusCode();  // 상태 코드 꺼내기
String body = response.getBody();                   // 응답 바디 꺼내기
int statusCodeValue = response.getStatusCodeValue();

나중에 필요할 것 같아서...

 

매커니즘은 대충 이렇다.

1. 프론트에서 헤더에 유저의 이메일과 재학생 여부를 담은 AccessToken이 포함된 요청 보낸다. (나중에 변할 수 있음 그러면 코드도 수정해야겠지흑흑)

2. 컨트롤러 -> 서비스 순으로 요청을 요청을 넘기고, 서비스에서 JWT 필터로 요청을 넘긴다.

3. JWT 필터가 헤더에서 AccessToken을 떼어내고 null일 경우 (null인 경우에는 헤더에 AccessToken이 안왔거나 없을 때) null을 반환한다 -> 서비스에서 null이 반환되면 ResponseBody에 응답코드로 에러를 띄운다.

4. AccessToken이 null이 아닐 경우 AccessToken의 신뢰성 조사를 위해 JWT Provider에게 넘겨 validateToken에게 신뢰성 검사를 맡긴다. "성공"이 반환되지 않을 경우 오류메시지를 반환해 서비스에서 ResponseBody에 응답코드로 에러를 띄운다.

5. 신뢰성 조사까지 통과할 경우 JWT토큰에 있는 유저정보를 JSON 형태로 반환함

6. 서비스나 컨트롤러에서 이상이 없을 경우 (응답코드 200 등을 받을 경우) 해당 요청을 승인함

 

이정도로 되겠다.. 생각보다 오래걸렸다. JWT 개념도 생소하고 AccessToken 발급도 생소하고 암튼

AccessToken의 유효기간은 3백만일로 설정해두었다. RefreshToken 기능을 만들기 전까지는 우선은 이렇게 할 예정 8200년 정도는 사용할 수 있다고 한다. 탈취될 경우 큰일날듯

 

테스트해봐야하는데 귀찮다 서비스와 컨트롤러까지 만들어야해서..

우선은 다음꺼부터 할 예정


2. @Value 어노테이션이 작동하도록 고치기

구글링해도 다 나와 상황이 다른데 딱 하나 나와 비슷한 경우를 찾았다.

 

https://velog.io/@jduckling_1024/Value%EB%A1%9C-application.yml%EC%9D%98-%EA%B0%92-%EA%B0%80%EC%A0%B8%EC%98%A4%EA%B8%B0

 

@Value로 application.yml의 값 가져오기

생성자 주입 시 자동으로 주입되지 않는 기본 자료형과 문자열의 값을 설정한다.@ConfigurationProperties 어노테이션을 달고있는 클래스의 필드에 값을 저장하고 그 값을 사용하는 방법도 여기에 있

velog.io

@Value를 인자로 주면 해결된다는데 그래서 인자로 @Value 값을 주도록 수정했다.

그리고 @Value 위치가 jwt.secret이 아니라 spring.jwt.secret이었다. 그래서 빈을 주입할 수 없던 오류 뿐 아니라 placeholder 오류도 떴던 것. 암튼 해결

 

저 어노테이션을 어떻게 넣나 했는데

/**
 *
 */

package com.hongchelin.controller.login;

import com.hongchelin.Service.login.loginSuccessService;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@RestController
public class loginSuccessController {
    private loginSuccessService loginSuccessService;

    @Autowired
    public loginSuccessController(loginSuccessService loginSuccessService) {
        this.loginSuccessService = loginSuccessService;
    }

    @GetMapping("/api/login/success")
    public ResponseEntity<Map<String, Object>> lsController(@Value("${spring.jwt.secret}") String secret, @AuthenticationPrincipal OAuth2User oauth2user, HttpServletRequest request) {
        String email = oauth2user.getAttribute("email");
        System.out.println(email);
        return loginSuccessService.checkUserInformation(secret, oauth2user);
    }
}
/**
 * ismemeber - true -> DB에 userEmail이 조회됨 / 반대는 false
 * response -> 응답코드
 *
 * 다음으로 이동해야할 것
 * response == 200  ->  main
 * response == 201  ->  회원가입창
 */

package com.hongchelin.Service.login;

import com.hongchelin.Service.JWT.JWTProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
@Component
public class loginSuccessService {
    private JWTProvider jwtService;
    public loginSuccessService() {
        this.jwtService = new JWTProvider();
    }

    public ResponseEntity<Map<String ,Object>> checkUserInformation(String secret, OAuth2User oauth2user) {
        boolean ismember = true;
        Map<String ,Object> response = new HashMap<String ,Object>();
        // DB와 대조하는 프로그램 작성 필요

        String userEmail = oauth2user.getAttribute("email");
        String roles = "outer";

        if (ismember) {
            response.put("code", 200);
            response.put("message", "signed member");
            response.put("redirect","/");

            String userToken = jwtService.createToken(secret, userEmail, roles);
            response.put("accesstoken", userToken);
        }
        else {
            response.put("code", 250);
            response.put("message", "unsigned member");
        }

        return ResponseEntity.status(HttpStatus.OK).body(response);
    }
}

그냥 컨트롤러에서 처음 어노테이션을 사용하고 그 뒤로는 String secret 으로 넘겨주기만 하면 됐다.

메서드마다 있는 String secret이 secret 코드.

 

https://www.youtube.com/watch?v=ZG85Z_4sh0s

 

이거 29초 실행한다고 며칠을 개발에만 시간을 쓰다니...

암튼 이정도로 구글 로그인은 잠시 마무리해도 될 듯하다. DB연동 때 다시 보는걸로

 

저번에 올린 영상과 달라진 점은 ismember가 true로 변경되어 이미 회원인 계정으로 인식되었다는 것 정도?


3-1. 네이버 로그인 만들기

이제 네이버 로그인을 해 볼 차례

약간의 링크만 다를 뿐 거의 코드복붙이라고 믿는다. 게다가 oauth 인증키는 스프링이 발급해주고, 발급 이후 과정은 지금 다 만들어놨으니...

 

security:
  oauth2:
    client: 
      registration:
        google:
          client-id: #
          client-secret: #
          scope:
            - profile
            - email
          redirect-uri: "http://localhost:8080/login/oauth2/code/google"
          authorization-grant-type: authorization_code
          client-name: Google
        naver:
          client-id: #
          client-secret: #
          scope:
            - profile
            - email
            - age
          redirect-uri: "http://localhost:8080/login/oauth2/code/naver"
          authorization-grant-type: authorization_code
          client-name: naver

      provider:
        google:
          authorization-uri: https://accounts.google.com/o/oauth2/auth
          token-uri: https://oauth2.googleapis.com/token
          user-info-uri: https://www.googleapis.com/oauth2/v3/userinfo
          user-name-attribute: sub
        naver:
          authorization-uri: https://nid.naver.com/oauth2.0/authorize
          token-uri: https://nid.naver.com/oauth2.0/token
          user-info-uri: https://openapi.naver.com/v1/nid/me
          user-name-attribute: response
public ResponseEntity<Map<String, Object>> naverlogin() {
    return ResponseEntity
            .status(HttpStatus.FOUND)
            .location(URI.create("/oauth2/authorization/naver"))
            .build();
}

대충 yml 파일 설정하고 돌리니 로그인까지는 된다.

근데 이메일을 파싱해오는데 null이 오네? 파싱하는 방법을 구글과 동일하게 했더니 오류가 생기나보다. 이럼 귀찮은데..

 

GPT에게 물어보니

구글 OAuth는 

{
name : "dx2d2y"
email : "dx2d2y@gmail.com"
}

이런 형식인데 네이버는

{
"resultcode" : 200
"message" : "success"
"response": {
	name : "dx2d2y"
	email : "dx2d2y@gmail.com"
}
}

뭐 이런식이란다, (JSON 표기법에서 오류가 생겨도 그정도는 넘어갑시다)

즉, 구글과 네이버에 따라 파싱하는 법이 다르단거.

 

그래서 oauth 인증을하고 얻은 객체를 출력해보니..

attributes = {resultcode=00, message=success, response={id=ID, age=20-29}}

헐.. 이메일 요청을 안했다..

그런데 알고보니 네이버 정책 변경으로 이메일을 주지 않는다. 따라서 식별자 (response에 담아주는 id)를 통해 사람을 분리해야한다. 그럼 DB에 구글용 이메일주소와 네이버용 ID가 섞일텐데 하나로 정해야할 것 같다.

https://developers.naver.com/forum/posts/33256

 

개발자 포럼 - NAVER Developers

 

developers.naver.com

@Service
public class loginSuccessService {
    @Autowired
    private JWTProvider jwtService;
    public loginSuccessService(JWTProvider jwtService) {
        this.jwtService = jwtService;
    }

    public ResponseEntity<Map<String ,Object>> checkUserInformation(String secret, OAuth2User oauth2user, OAuth2AuthenticationToken request) {
        boolean ismember = true;
        String registrationId = request.getAuthorizedClientRegistrationId();
        Map<String ,Object> response = new HashMap<String ,Object>();
        // DB와 대조하는 프로그램 작성 필요

        String email;
        String userToken;
        String roles = "outer";

        if (registrationId.equals("google")) {
            email = oauth2user.getAttribute("email");
            userToken = jwtService.createToken(secret, email, roles);
        } else {
            Map<String, Object> userInfo = oauth2user.getAttributes();
            Map<String, Object> claims = (Map<String, Object>) userInfo.get("response");
            String id = claims.get("id").toString();

            userToken = jwtService.createToken(secret, id, roles);
        }

        if (ismember) {
            response.put("code", 200);
            response.put("message", "로그인 성공. 외부인 계정으로 로그인됨");
            response.put("redirect","/");

            response.put("accesstoken", userToken);
        }
        else {
            response.put("code", 250);
            response.put("message", "로그인 성공. 비회원이기에 회원가입창으로 이동합니다.");
        }

        return ResponseEntity.status(HttpStatus.OK).body(response);
    }
}

 

그래서 우선 개발했다. 코드는 잘 작동한다.

우선 식별자가 이메일(구글), ID(네이버)로 나뉘기 때문에 이에 따라 나눌 필요가 있어 registrationId로 구글 로그인인지 네이버 로그인인지 구분부터 했다. 그냥 이메일 / ID를 oauth 객체에서 따온 후 재학생 여부와 함께 JWT 토큰을 부여한다. 아직 예외처리는 안했다.

 

https://youtu.be/lb4agsRtn2E

네이버 로그인 작동

..확실히 백엔드로 개발하니까 한거에 비해서 결과물이 아쉽다ㅋㅋ 그래도 더 보람차긴 함


3-2. 카카오 로그인 만들기

카카오 로그인도 쉽게 끝냈다.

security:
  oauth2:
    client:
      registration:
        google:
          client-id: //
          client-secret: //
          scope:
            - profile
            - email
          redirect-uri: "http://localhost:8080/login/oauth2/code/google"
          authorization-grant-type: authorization_code
          client-name: Google
        naver:
          client-id: //
          client-secret: //
          scope:
            - name
            - age
          redirect-uri: "http://localhost:8080/login/oauth2/code/naver"
          authorization-grant-type: authorization_code
          client-name: naver
        kakao:
          client-id: //
          client-secret: //
          scope:
            - profile_nickname
          redirect-uri: "http://localhost:8080/login/oauth2/code/kakao"
          authorization-grant-type: authorization_code
          client-name: kakao

      provider:
        google:
          authorization-uri: https://accounts.google.com/o/oauth2/auth
          token-uri: https://oauth2.googleapis.com/token
          user-info-uri: https://www.googleapis.com/oauth2/v3/userinfo
          user-name-attribute: sub
        naver:
          authorization-uri: https://nid.naver.com/oauth2.0/authorize
          token-uri: https://nid.naver.com/oauth2.0/token
          user-info-uri: https://openapi.naver.com/v1/nid/me
          user-name-attribute: response

        kakao:
          authorization-uri: https://kauth.kakao.com/oauth/authorize
          token-uri: https://kauth.kakao.com/oauth/token
          user-info-uri: https://kapi.kakao.com/v2/user/me
          user-name-attribute: id

yml 파일.

카카오는 시크릿키가 없어도 잘 작동되더라. 오히려 GPT가 시크릿키 빼라고 조언까지 해줬다. 싱기방기

 

https://youtu.be/QtMliQitieU

작동영상

구글 / 네이버 / 카카오 로그인 기능을 구현하고, 응답코드와 accessToken을 반환하는 코드다.


이렇게 소셜로그인 기능은 전부 마무리!

그리고 부가적으로.. 지금 AccessToken의 유효기간을 3백만일로 설정해놨는데.. RefreshToken 기능의 뼈대만 세워보기로..

 

추가적으로 AccessToken의 만료에 대해 생각해보면...

최초 로그인 시 백엔드에서 AccessToken을 발급해준다. --> 비회원기능에서는 AccessToken을 보낼 필요 X

AccessToken의 유효기간이 지나면, 회원서비스를 이용할 수 없다. (401 에러반환)

그럴경우, RefreshToken을 통해 새로운 AccessToken을 발급하면 끝

 

RefreshToken의 기능은 DB가 생겨야하기 때문에... 그건 빼고 만들기로


RefreshToken의 발급플로우는 대략 이렇다.

 

1. 프론트에서 AccessToken이 만료될 경우 /api/login/expired 로 리다이렉트 시킨다.

2. 백에서는 해당 url에 요청이 오면 RefreshToken을 

package com.hongchelin.Service.JWT;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

@Service
public class JWTExpiredService {
    private final JWTFilter jwtFilter;

    @Autowired
    public JWTExpiredService(JWTFilter jwtFilter) {
        this.jwtFilter = jwtFilter;
    }

    public ResponseEntity<?> getJWTRefresh(String secret) {
        String refreshToken = "hi!";
        boolean refreshTokenValidity = jwtFilter.checkValidityInTrueFalse(secret, refreshToken);

        if (refreshTokenValidity) { //토큰 유효 / 새 AccessToken 발급
            Map<String, Object> userData = new HashMap<>();
            userData = jwtFilter.getUserRolesInJSON(secret, refreshToken);
            String userEmail = (String) userData.get("userEmail");
            String userRole = (String) userData.get("userRole");
            String newAcessToken = jwtFilter.createToken(secret, userEmail, userRole);

            Map<String, Object> response = new HashMap<>();
            response.put("code", 200);
            response.put("token", newAcessToken);

            return ResponseEntity
                    .status(HttpStatus.OK)
                    .body(response);
        } else {
            Map<String, Object> response = new HashMap<>();
            response.put("code", 401);
            response.put("message", "JWT Refresh Token Expired");
            response.put("redirect_uri", "/login");

            return ResponseEntity
                    .status(HttpStatus.UNAUTHORIZED)
                    .body(response);
        }
    }

}

Refresh Token 코드

 

암튼 여기까지 했는데 secret키 인코딩과 관련해서 복잡한 문제가 개많이 쌓여서 여기까지.. 우선은 내일할 예정이다.

 

내일 할 거

secret 인코딩 관련해서 오류난거 해결

홍익대 재학생 로그인 기능 구현

끗

'팀 프로젝트 > [2025][GDG]홍대 맛집 아카이빙 프로젝트' 카테고리의 다른 글

[GDG]홍대 맛집 아카이빙 프로젝트 백엔드 개발 #5 - OAuth 최종점검  (0) 2025.07.18
[GDG] 개발코스 2주차 WIL  (1) 2025.07.17
[GDG]홍대 맛집 아카이빙 프로젝트 백엔드 개발 #3 - 구글 OAuth  (0) 2025.07.16
[GDG] 홍대 맛집 아카이빙 프로젝트 백엔드 개발 #2 - ResponseEntity 활용하기  (1) 2025.07.15
[GDG] 홍대 맛집 아카이빙 프로젝트 백엔드 개발 #1 - OAuth에 대해서...  (2) 2025.07.14
'팀 프로젝트/[2025][GDG]홍대 맛집 아카이빙 프로젝트' 카테고리의 다른 글
  • [GDG]홍대 맛집 아카이빙 프로젝트 백엔드 개발 #5 - OAuth 최종점검
  • [GDG] 개발코스 2주차 WIL
  • [GDG]홍대 맛집 아카이빙 프로젝트 백엔드 개발 #3 - 구글 OAuth
  • [GDG] 홍대 맛집 아카이빙 프로젝트 백엔드 개발 #2 - ResponseEntity 활용하기
Radiata
Radiata
개발을 합니다.
  • Radiata
    DDD
    Radiata
  • 전체
    오늘
    어제
    • 분류 전체보기 (211)
      • 신년사 (3)
        • 2025년 (2)
        • 2026년 (1)
      • CS (59)
        • JVM (12)
        • 백엔드 (20)
        • 언어구현 (1)
        • 객체지향 (1)
        • 논리회로 (5)
        • 컴퓨터구조 (9)
        • 데이터베이스 (1)
        • 컴퓨터 네트워크 (10)
      • 언어공부 (64)
        • Java | Kotlin (48)
        • JavaScript | TypeScript (9)
        • C | C++ (6)
      • 개인 프로젝트 (11)
        • [2025] Happy2SendingMails (3)
        • [2026] 골든리포트! (8)
        • [2026] 순수자바로 개발하기 (0)
        • 기타 이것저것 (0)
      • 팀 프로젝트 (29)
        • [2025][GDG]홍대 맛집 아카이빙 프로젝트 (29)
      • 알고리즘 (13)
        • 백준풀이기록 (11)
      • 놀이터 (0)
      • 에러 수정일지 (2)
      • 고찰 (24)
        • CEOS 23기 회고록 (2)
  • 블로그 메뉴

    • CS
    • 언어공부
    • 개인 프로젝트
    • 팀 프로젝트
    • 알고리즘
    • 고찰
    • 신년사
    • 컬러잇 개발블로그
  • 링크

    • 컬러잇 개발블로그
  • 공지사항

  • 인기 글

  • 태그

    144
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
Radiata
[GDG]홍대 맛집 아카이빙 프로젝트 백엔드 개발 #4 - 오늘은 OAuth 끝내죠?
상단으로

티스토리툴바