일친 (IlChin) - 회원 로그인, 회원가입(2)

no.oneho·2025년 5월 30일
0

일친 개발기

목록 보기
7/17

이번 포스팅에서는 드디어 회원 인증/인가 부분인 로그인을 구현하도록 하겠다

먼저 다른 일반적인 방식과는 특이하게 UserDetailsService를 상속받지않고 구현하는것으로 포스팅을 하겠다!

우선 암호화 키를 하나 설정해준다.
다만 이 암호화 키 또한 환경변수로 둬서 노출가능성을 최소화하는 방식으로 하겠다.


실행환경에 환경변수를 설정해두고 (지금 방식은 인텔리제이에서 로컬 실행용으로만 사용하는 환경변수 설정이다)

이후 application.yml 에 명시해주고 코드에서 사용하면 된다!

이제 토큰로그인 방식으로 구현하기위해 tokenProvider 클래스를 하나 만들어준다.

@Service
public class TokenProvider {

    private static String SECRET_KEY;

    @Value("${secret-key-source}")
    public void setSecretKey(String secretKey) {
        SECRET_KEY = secretKey;
    }

    public String createToken(User user) {
        return null;
    }

    private static Claims extractClaims(String token) {
        return null;
    }
    
}

이게 기본 구조이다. createToken은 토큰을 만드는 메서드, extractClaims은 토큰에서 정보를 추출하는 메서드 라고보면된다.

이제 뭘 구현할지 하나씩 생각해보자
우리가 사용할 jwt 토큰에 필요한 필수정보는 유저의 식별값, 유저의 아이디, 만료기한
이정도가 될 수 있다.

쉽게 생각하면 토큰을 만들고 토큰에서 정보를 빼내올 때 어떤 정보를 나중에 사용할지 고민하면 스펙에 맞게 설계하고 코드를 작성하면 된다.

그럼 유저의 식별값과 아이디부터 해보자

식별값과 아이디의 경우 createToken 메서드에서 인자로 받는 User 객체안에 들어있을거기 때문에 간단하다.

public String createToken(User user) {

        Claims claims = Jwts.claims();
        claims.put("id", user.getId());
        claims.put("username", user.getUsername());

        return null;
    }

그리고 그 다음은 만료기한이다.

public String createToken(User user) {
        Date expiryDate = Date.from(
                Instant.now()
                        .plus(1, ChronoUnit.DAYS)
        );
        
        Claims claims = Jwts.claims();
        claims.put("id", user.getId());
        claims.put("username", user.getUsername());

        return null;
    }

이렇게 만들면 jwt 토큰에는 식별값, 아이디, 만료기한이 들어있게 된다. 이제 이걸 토큰 형식으로 변환해서 return 해주면된다.

public String createToken(User user) {
        Date expiryDate = Date.from(
                Instant.now()
                        .plus(1, ChronoUnit.DAYS)
        );

        Claims claims = Jwts.claims();
        claims.put("id", user.getId());
        claims.put("username", user.getUsername());

        return Jwts.builder()
                .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
                .setClaims(claims)
                .setExpiration(expiryDate)
                .compact();
    }

토큰 생성부분 코드이다. 이제 이걸 테스트코드로 잘 나오는지 테스트해보자

TokenTest.class

public class TokenTest {

    private TokenProvider tokenProvider;

    @BeforeEach
    public void setUp() {
        tokenProvider = new TokenProvider();
        tokenProvider.setSecretKey("test");
    }

    @Test
    public void 토큰생성_테스트() {
        User user = User.builder()
                .id(1L)
                .username("test")
                .password("<PASSWORD>")
                .build();
        String token = tokenProvider.createToken(user);
        System.out.println(token);
    }



}

결과도 잘 나오는걸 확인 할 수 있다.

근데 토큰만 있으면 실제로 넘어간 데이터가 잘 저장됐는지 확인이 안되지않나?
그 테스트는 이제 정보를 추출할 메서드를 마저 만든 후 그 메서드를 테스트하며 확인해볼것이다.

public Claims extractClaims(String token) {
        return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
    }

이러한 정보추출 메서드를 만들었다.
접근제한은 테스트에서 사용하기위해 일단 public
테스트가 확인되면 다시 prvaite 으로 변경할것이다.

이제 이 코드또한 테스트코드로 돌려보자

TokenTest.class

public class TokenTest {

    private TokenProvider tokenProvider;
    private String token;
    private final User user = User.builder()
            .id(1L)
            .username("test")
            .password("<PASSWORD>")
            .build();

    @BeforeEach
    public void setUp() {
        tokenProvider = new TokenProvider();
        tokenProvider.setSecretKey("test");
        token = tokenProvider.createToken(user);
    }

    @Test
    public void 토큰정보_확인_테스트() {
        Claims claims = tokenProvider.extractClaims(token);
        assertEquals(Long.valueOf(claims.get("id").toString()), user.getId());
        assertEquals(claims.get("username").toString(), user.getUsername());
    }
}

token 생성의 선행작업을 위해 생성 테스트 코드를 BeforeEach로 위치를 변경하였다.

테스트 결과도 성공이므로 로그인했을때 정보를 담고있는 jwt 토큰을 생성하는데는 성공적이다!

profile
이렇게 짜면 요구사항이나 기획이 변경됐을 때 불편하지 않을까? 라는 생각부터 시작해 설계를 해나가는 개발자

0개의 댓글