Post

real-world프로젝트 Spring 시작하기(4) - 회원가입 만들기 전 설정 및 의존성 파일들 설명.

real-world project 구현 과정 카테고리 보기

백엔드 명세서를 보면 Authentication : POST /api/users/login 과 Registration POST /api/users에 겹치는 url이 존재한다. /api/users다.

이를 prefix로 해주는 방법이 있을거라 생각해봤다.

  • 1번 @RequestMapping()안에다가 /api/users까지만 적기
  • 2번 application.propeties설정 파일에 설정하기.
1
 server.servlet.contextPath=/api     를 적어주면 된다.

처음에는 2번의 경우로 했으나, Webconfig에 허용범위설정 등을할 때 내가 계속 prefix를 적어놨다는걸 까먹어서 에러를 많이 발생시켰다. 예를 들어서 WebConfig설정 파일 중 다음과 같은 코드를 적을 수 있다.

1
 .antMatchers("/api/users/**").permitAll()

이 뜻은 ‘/api/users/~~~’로 들어오는 모든것들에 대해 인증없이 요청을 허용한다는 것인데 2번처럼 설정파일에 prefix를 설정해버리면

1
 .antMatchers("/users/**").permitAll()

로 적어줘야한다. 설정파일의 prefix가 우선이다. 이걸로 몇 번을 해맸고, 굳이 이렇게할거면 API명세는 왜 있겠는가 생각하여 1번 방식으로 바꿔버렸다.

그래서 그냥 1번 방법으로 했다. WebConfig에 관해서는 뒤에서 또 다뤄볼 것이다.

명세에서 보면 회원가입시 jwt를 이용해서 token도 포함한 객체를 리턴해야하는데 이는 다른 포스팅에서 다루기로 하고 설정을 먼저 보겠다.

먼저, 의존성을 살펴보자. 나의 프로젝트 환경이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Spring : 2.7.3
java : 18 
dependencies {
    org.springframework.boot:spring-boot-starter-validation'
    implementation 'org.springframework.boot:spring-boot-starter-security'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-test'
    implementation 'org.projectlombok:lombok:1.18.24'
    implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
    implementation 'io.jsonwebtoken:jjwt-impl:0.11.5'
    implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5'

    annotationProcessor 'org.projectlombok:lombok'
    implementation group: 'org.bgee.log4jdbc-log4j2', name: 'log4jdbc-log4j2-jdbc4', version: '1.16'
    // inmemory
    runtimeOnly 'com.h2database:h2'


    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'org.springframework.boot:spring-boot-starter-jdbc:2.7.3'
    testImplementation 'org.springframework.security:spring-security-test'
}

참고로 이는 더 추가되고 있다.

Spring 2.7.3 버전과 java 18을 쓴 건 그냥 최신버전이라 쓴거다.

근데 Spring은 .. 설정 부분이 많이 바뀌었더라. 특히 deprecated된 것들 처리하느라 애먹었다.

자바와 스프링 설명을 이렇게 해두고 dependencies를 하나씩 뜯어보겠다.

spring-boot-stater-security

많은것을 포함하고 있지만, 인증을 구현하려면 이 프레임워크가 가지고 있는 것들을 사용해야한다.

예를들면 위에서 말한 WebConfig 설정을 해줘서 요청에대해 인가하거나 인가하지 않거나를 정할 수 있다.

이 프레임워크를 dependency 하면 모든 요청에 인증이 필요해져서 설정을 따로 해줘야한다.

spring-boot-starter-validation

대표적으로 @Valid라는 어노테이션을 쓰기위함이다. @Valid는 이름에서도 알 수 있게 검증을 한다. 여기서는 @RequestBody와 함께 쓰여 요청 객체를 검증할 것이다. 검증방식은 객체DTO에 따로 정의해야한다. NotNULL어노테이션을 달거나, @Email 어노테이션을 다는 필드에 맞는 어노테이션을 달아주면 알아서 검증을 해준다.

실제 코드에서는 이렇게 쓰인다.

1
2
3
public UserResponse signup(@Valid @RequestBody  UserSignupRequest userSignupRequest) {
    return userService.signup(userSignupRequest);
}

이러면 UserSignupRequest에 검증 방식이 들어있어야한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Getter
@AllArgsConstructor
@Builder
@JsonTypeName("user")
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
public class UserSignupRequest {

    @NotBlank
    private String username;
    @Email
    private String email;
    @NotBlank
    private String password;
}

Json~어노테이션을 무시하길 바란다. 나중에 또 복기할 것이다.

이렇게 적어놓으면 username,과 password는 빈 값, null이 들어올 수 없고 email은 Email형식을 지키게 해놓는다. 참고로 @Email 어노테이션은 null을 허용한다.

spring-boot-starter-data-jpa

JPA를 좀 더 쉽게 다루기 위함이다. 처음에는 공부의 목적으로 쿼리를 직접 작성하여 모든걸 구현할 생각이었지만, 생산성도 크게 줄어들고 자료가 잘 나와있지 않다.. 구글링하면 대부분이 Spring-data-jpa를 기본값으로 쓴다. 순수 jpa를 공부하려면 김영한 선생님의 책을 사야할 것 같다.

spring-boot-starter-web

Web과 관련되어있는 프레임워크다. 이를 사용하지 않으면 Filter를 구현할 수 없고, HttpServletRequest, HttpServletResponse 등을 사용할 수 없다. real-world는 Web Application 구현에 목적을 두고 있으니 사용하는게 맞다. 또한 API 호출을 위해선 당연히 사용해야한다.

spring-boot-starter-test

이 프레임워크를 사용하면 @SpringBootTestWebMvcTest 등을 사용할 수 있는데, 향후 코드에서 사용할 것이므로 그때가서 자세히 복기할 것이다.

lombok

적절한 롬복 사용은 사랑이라는 것을 알았다.

jjwt-api:0.11.5

jjwt-apiClaims, 예외처리인 ExpiredJwtException, Jwts, Keys..등 이라는 객체를 가지고 있다.

  • Claims는 토큰에 담는 정보이다.
  • ExpiredJwtException은 토큰을 생성할 때의 지정한 유효기간이 지났는지 확인하고 지났을 경우 예외를 발생시키는 Exception이다.
  • Jwts는 jwt토큰을 builder를 통해 만들거나 parser를 통해 정보를 뽑아내는 객체이다. 참고로 parser()는 deprecated되어서 parserBuilder()를 사용해야한다. 이도 애먹었다. 아마 많은곳들에서는 0.9버전을 사용하는데 여기는 parser()가 사용가능할 것이다.

왜 Deprecated되었는지는 찾아보고 나중에 복기할 때 쓸 수 있으면 쓰겠다.

jjwt-impl:0.11.5

이거 없으면 에러낸다. 위의 jjwt-api에서 정의되어 있는 Claims의 코드를 까보면 다음과 같이 생성자가 정의 되어있다.

1
2
3
public static Claims claims() {
    return Classes.newInstance("io.jsonwebtoken.impl.DefaultClaims");
}

이는 jjwt-impl에 정의되어있는 클래스를 사용하겠다는 뜻이다.

참고로 import되어 있지 않아도 Spring이 친절하게 설명해주므로 걱정하지말라.

1
2
에러내용
io.jsonwebtoken.lang.UnknownClassException: Unable to load class named [io.jsonwebtoken.impl.DefaultClaims] from the thread context, current, or system/application ClassLoaders.  All heuristics have been exhausted.  Class could not be found.  ->>Have you remembered to include the jjwt-impl.jar in your runtime classpath?<<-

jjwt-jackson

이게 있어야 JJWT가 토큰을 생성할때 자동으로 Jackson 라이브러리를 가져다 JSON매핑을 한다고 한다. 그러므로 이도 필요한것. 이게 없으면 또 에러난다. 이는 JSON에 대해서 에러가 나는게 아니고, Serializer를 못해서 나는 에러같다. 토큰을 만들때 compact()함수를 호출해야하는데, 이 부분에서 Serializer를 하기 때문이다. 커스텀으로 따로 구현하거나 맞는 구현체를 끼워넣으라고 한다.

1
2
에러내용
Unable to find an implementation for interface io.jsonwebtoken.io.Serializer using java.util.ServiceLoader. Ensure you include a backing implementation .jar in the classpath, for example jjwt-impl.jar, or your own .jar for custom implementations.

implementation group: ‘org.bgee.log4jdbc-log4j2’, name: ‘log4jdbc-log4j2-jdbc4’, version: ‘1.16’

log4j와 jdbc를 연결하여 만들어진 오픈소스 프로젝트로, DB관련 로그를 수집하거나 필터링해서 필요한 로그만 출력한다. 파일로 로그를 남길 수도 있다. 필요없다면 빼도 된다.

testImplementation

testImplementation는 테스트 코드를 수행할 때만 적용하는 라이브러리다.

여기서 새로 등장하는건 spring-security-test이다.

spring-security-test

아직 공부중이다. 통합테스트를 진행한다고 할 때 유저 인증은 어떻게 할것인가? 필터는 어떻게 적용되고? 이를 테스트하기 위한 환경을 구축하는데 도와주는 하위 프레임워크다. 임시로 인증을 하거나 인증을 무시할 수도 있다.

여러 테스트환경을 구축할 수 있게 도와준다. 이거 나중에 진짜 골머리 아프다.

현재 이 프로젝트는 vue.js + vite + vuex + Spring boot, Spring Data JPA + Spring Security 를 이용하여 real-world 데모버전을 제작완료하였습니다. https://github.com/kkminseok/real-world-springboot

This post is licensed under CC BY 4.0 by the author.