real-world프로젝트 Spring 시작하기(4) - 회원가입 만들기 전 설정 및 의존성 파일들 설명.
백엔드 명세서를 보면 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
이 프레임워크를 사용하면 @SpringBootTest
와 WebMvcTest
등을 사용할 수 있는데, 향후 코드에서 사용할 것이므로 그때가서 자세히 복기할 것이다.
lombok
적절한 롬복 사용은 사랑이라는 것을 알았다.
jjwt-api:0.11.5
jjwt-api
는 Claims
, 예외처리인 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