2024. 2. 19. 21:17γBackend/πΏ Spring
λͺ©μ°¨
- 0. JDK
- 1. javax -> jakarta
- 2. querydsl
- 3. spring security
νμ¬ Tidify iOS μ±μ μ΄μ μ€μ΄λ€. μλ²λ μ€νλ§λΆνΈ 2.7.8 λ²μ μ μ¬μ© μ€μ΄μλλ° μ°ν΄κΈ°κ° λμ λ²μ μ μ μ§ννλ€.
λ²μ μ μ μ§ννλ©΄μ λ€μν λ²½(?)μ λΆλͺνλλ° κ·Έ κ³Όμ μ νλνλ νμ΄λ³΄κ² λ€.
μ€νλ§λΆνΈ 3 μλ λ€μν λΌμ΄λΈλ¬λ¦¬κ° μΆκ°λμ§λ§ λ€μμ ν° νΉμ§μ΄ μλ€.
- Java17κ³Ό Java19 μ§μ (Java 21 λ μΆκ°)
- GraalVM μ§μ
- Spring framework 6.0 κΈ°λ°
0. jdk
μ€νλ§λΆνΈ 3μ java17 μ κΈ°λ°μΌλ‘ μμ±λκΈ° λλ¬Έμ 17 λ―Έλ§μ JDKλ μ§μνμ§ μλλ€.
κΈ°μ‘΄μλ jdk λ java 17 μ μ¬μ©νκ³ μμκΈ°μ λ³λλ‘ jdk μ λ°μ΄νΈλ νμ§ μμλ€.
1. javax -> jakarta
μ€νλ§λΆνΈ 3 λΆν΄ JavaEE κ° JakartaEE λ‘ λ³κ²½λλ©΄μ javax.* λ‘ λͺ λͺ λ ν¨ν€μ§λ₯Ό λͺ¨λ jakarta.* λ‘ μμ ν΄μΌ νλ€.
- Java Servlet(javax.servlet) -> Jakarta Servlet(jakarta.servlet)
- Java Message Servie (javax.jms) -> Jakarta Messaging (jakart.jms)
- JPA:Java Persistence API (javax.persistence) -> Jakarta Persistence(jakarta.persistence)
- JTA:Java Transaction API (javax.transaction) -> Jakarta Transaction(jakarta.transaction)
- Java Mail (javax.mail) -> Jakarta Mail (jakarta.mail)
νμλ IntelliJ IDE λ₯Ό μ¬μ©νκ³ μμ΄ κ°λ¨νκ² cmd + shifth + R λͺ λ Ήμ΄λ‘ javax λ₯Ό λͺ¨λ jakarta λ‘ μμ νλ€.
νμμ νλ‘μ νΈκ° μκ·λͺ¨λΌ μ΄λ°μμΌλ‘ javax ν¨ν€μ§λ₯Ό -> jakarta ν¨ν€μ§λ‘ μμ νλλ° λκ·λͺ¨ νλ‘μ νΈμμλ μ΄λ° μμΌλ‘ ν¨ν€μ§λ₯Ό μμ ν΄λ λ μ§ λͺ¨λ₯΄κ² λ€.
2. querydsl
μ°μ build.gradle μμ querydsl νλ¬κ·ΈμΈμ μ κ±°νλ€. μ΄ λΆλΆμ μ κ±°νμ§ μμμ μ»΄νμΌ ν λ μ§μμ μΌλ‘ μλ¬κ° λ°μνλ€.
plugins {
id 'java'
id 'org.springframework.boot' version '2.7.8'
id 'io.spring.dependency-management' version '1.0.15.RELEASE'
// querydsl μ£Όμ μ²λ¦¬
// id "com.ewerk.gradle.plugins.querydsl" version "1.0.10"
...
}
dependencies λΈλμλ jakarta ν¨ν€μ§μ μμ‘΄μ±μ μΆκ°νλ€.
dependencies {
...
// querydsl κΈ°μ‘΄ λνλμ
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
annotationProcessor 'com.querydsl:querydsl-apt:5.0.0:jakarta'
// μΆκ°λ λνλμ
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"
}
dependencies κΉμ§ μΆκ°ν νμλ build.gradle νμΌ νλ¨μ μλμ λ΄μ©μ μΆκ°νλ€.
νμλ build/generated κ²½λ‘ νμμ querydsl λΉλ νμΌμ μμ±νλλ‘ μ€μ νλ€. μ΄ λΆλΆμ κ°κ°μΈ λ§λ€ μ°¨μ΄κ° μμ΄ κ°λ³ νλ‘μ νΈ μ€μ μ λ§κ² λ³κ²½ν΄μΌνλ€.
κ°μ λ²μ
// λ²μ μ
def querydslDir = "build/generated/querydsl"
sourceSets {
main.java.srcDirs += [ querydslDir ]
}
tasks.withType(JavaCompile) {
options.generatedSourceOutputDirectory = file(querydslDir)
}
clean.doLast {
file(querydslDir).deleteDir()
}
sourceSets {
main.java.srcDir querydslDir
}
configurations {
querydsl.extendsFrom compileClasspath
}
κΈ°μ‘΄ λ²μ
// κΈ°μ‘΄
def querydslDir = "build/generated/querydsl"
querydsl {
jpa = true
querydslSourcesDir = querydslDir
}
sourceSets {
main.java.srcDir querydslDir
}
configurations {
querydsl.extendsFrom compileClasspath
}
compileQuerydsl {
options.annotationProcessorPath = configurations.querydsl
}
3. spring security
μ€νλ§λΆνΈ3.0 λΆν΄ μ€νλ§ μνλ¦¬ν° 6.0.0 μ΄μμ λ²μ μ΄ μ μ©λλ€. κΈ°μ‘΄ 2.7.3 μ΄ν λΆν° μνλ¦¬ν° μ€μ μ deprecated λ λ΄μ©μ΄ μΆκ°λμκ³ 3.0 λΆν°λ μμ μμ λ μ€μ λ μλ€.
λ²μ μ μ νκ³ λλμ μ μν리ν°μμ λλ€ DSL μ λ³΄λ€ μ κ·Ήμ μΌλ‘ λμ ν κ²μ΄λ€. 곡μλ¬Έμμμ μΈκΈνλ― μ€νλ§μνλ¦¬ν° 5.2 λ²μ λΆν° λλ€ DSL μ μ¬μ©ν μ μκ³ HttpSecurity μ€μ μ μ΄λ₯Ό μ μ©ν μ μλ€.
λλ€ DSL μ΄ μ μ©λ μ£Όμν μ΄μ λ₯Ό μμ½νμλ©΄ 1) λ¦¬ν΄ νμ μ΄ λͺ ννμ§ μλ€λ μ κ³Ό 2) λ²μ κ° μ½λ μΌκ΄μ± ν΅μΌμ μν΄μ μμ λλ€κ³ νλ€.
μλ μμ λ μ½λλ₯Ό 보μ. Boot 3.x μ΄μ μλ antMatchers(...), mvcMatchers(...) μ μΈν΄μ νΉμ 리μμ€μ λν μ κ·ΌκΆνμ λΆμ¬νμ§λ§, κ°μ λ²μ μμλ authorizeRequests() μμ λλ€λ‘ url request μ κΆνμ μ€μ νλ€.
λν Boot 3.x μ΄ν λΆν°λ antMatchers(), mvcMatchers() κ° μμ΄μ§κ³ requestMatchers() λ₯Ό μ¬μ©νλ€.
// κΈ°μ‘΄
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.httpBasic()
.and()
.authorizeRequests()
.antMatchers("/oauth2/login").permitAll()
.and()
.addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider), UsernamePasswordAuthenticationFilter.class);
}
// λ²μ μ
@Bean
protected SecurityFilterChain filterChain(HttpSecurity http, HandlerMappingIntrospector introspector) throws Exception {
http.csrf(AbstractHttpConfigurer::disable);
http.authorizeHttpRequests(authorize ->
authorize.requestMatchers(new MvcRequestMatcher(introspector, "/**")).permitAll()
.requestMatchers(new MvcRequestMatcher(introspector, "/oauth2/login")).permitAll()
.anyRequest()
.authenticated())
.httpBasic(Customizer.withDefaults());
return http.build();
}
λν κΈ°μ‘΄ λ²μ μμλ WebSecurityConfig μμ WebSecurityConfigurerAdapter λ₯Ό μμλ°μ μ¬μ©νμ§λ§ μ€νλ§ μνλ¦¬ν° 5.7λΆν° WebSecurityConfigurerAdapterκ° Deprecated λ¨μ λ°λΌ μ€λ²λΌμ΄λ©ν΄μ μ¬μ©ν configure() λ©μλλ₯Ό μ¬μ©ν μ μκ² λλ€.
λμ SecurityFilterChain Bean μ λ±λ‘ν΄ μ€μ ν μ μλ€.
JwtSecurityConfig
SecurityConfigurerAdapter λ₯Ό filterChain μ μ§μ μΆκ°ν μ μλ€. μ΄λ₯Ό μν΄ SecurityConfigureAdapter λ₯Ό μμλ°λ JwtSecurityConfig ν΄λμ€λ₯Ό μμ±νκ³ μ΄λ₯Ό filterChain μ μΆκ°νλ€.
νμ¬ νλ‘μ νΈλ jwt λ₯Ό μ΄μ©ν λ‘κ·ΈμΈμ ꡬννκΈ°μ JwtSecurityConfig ν΄λμ€λ₯Ό λ§λ€μ΄ SecurityConfigurerAdapter λ₯Ό μμλ°μλ€.
@RequiredArgsConstructor
public class JwtSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
private final JwtTokenProvider tokenProvider;
@Override
public void configure(HttpSecurity builder) throws Exception {
JwtAuthenticationFilter authFilter = new JwtAuthenticationFilter(tokenProvider);
builder.addFilterBefore(authFilter, UsernamePasswordAuthenticationFilter.class);
}
}
JwtAuthenticationFilter
νμ¬ νλ‘μ νΈλ OncePerRequestFilter λ₯Ό μμλ°λ Jwt μΈμ¦ νν°λ₯Ό μ¬μ©μ€μ΄κΈ°μ μ΄λ₯Ό configure method λ΄λΆμμ μ§μ νΈμΆνμ¬ filter μ μΆκ°νλ€. JwtTokenProvider λ jwt accessToken, refreshToken μ λ°κΈνλ μ©λμ ν΄λμ€λ€.
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtTokenProvider jwtTokenProvider;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String accessToken = resolveAccessToken(request);
... (λ΄λΆκ΅¬ν)
filterChain.doFilter(request, response);
}
}
μ΄μ JwtSecurityConfig ν΄λμ€λ₯Ό filterChain λ΄λΆμ μΆκ°νκΈ°λ§ νλ©΄ λλ€.
@Bean
protected SecurityFilterChain filterChain(HttpSecurity http, HandlerMappingIntrospector introspector) throws Exception {
http.csrf(AbstractHttpConfigurer::disable);
http.authorizeHttpRequests(authorize ->
authorize.requestMatchers(new MvcRequestMatcher(introspector, "/**")).permitAll()
.requestMatchers(new MvcRequestMatcher(introspector, "/oauth2/login")).permitAll()
.anyRequest()
.authenticated())
.httpBasic(Customizer.withDefaults())
.apply(new JwtSecurityConfig(jwtTokenProvider)); // JwtSecurityConfig μΆκ°
return http.build();
}
λμννλ©΄ μλμ κ°μ λͺ¨μ΅μΌλ‘ ν΄λμ€κ° μμ‘΄κ΄κ³κ° νμ±λλ€.
WebSecurityConfig μ½λ λΉκ΅ μ 리
μ΄μ λ²μ (μ€νλ§λΆνΈ v2.7.8)
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final JwtTokenProvider jwtTokenProvider;
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/api")
.antMatchers("/oauth2/login")
.antMatchers("/root");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.httpBasic()
.and()
.authorizeRequests()
.antMatchers("/oauth2/login").permitAll()
.and()
.addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider), UsernamePasswordAuthenticationFilter.class);
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
μ κ·Έλ μ΄λ λ²μ (μ€νλ§λΆνΈ v3.0.2)
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class WebSecurityConfig {
private final JwtTokenProvider jwtTokenProvider;
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.ignoring().
requestMatchers(new AntPathRequestMatcher("/api"))
.requestMatchers(new AntPathRequestMatcher( "/oauth2/login"))
.requestMatchers(new AntPathRequestMatcher( "/root"));
}
@Bean
protected SecurityFilterChain filterChain(HttpSecurity http, HandlerMappingIntrospector introspector) throws Exception {
http.csrf(AbstractHttpConfigurer::disable);
http.authorizeHttpRequests(authorize ->
authorize.requestMatchers(new MvcRequestMatcher(introspector, "/**")).permitAll()
.requestMatchers(new MvcRequestMatcher(introspector, "/oauth2/login")).permitAll()
.anyRequest()
.authenticated())
.httpBasic(Customizer.withDefaults())
.apply(new JwtSecurityConfig(jwtTokenProvider));
return http.build();
}
}
λ§λ¬΄λ¦¬
νμ¬ ν°λνμ΄ μ±μ μλ²λ νμμ μΌλ‘ μ€νλ§λΆνΈ 3.0μ΄ νμν μν©μ μλμμ§λ§, λ²μ μ κ·Έλ μ΄λλ₯Ό μ§ννλ©΄μ κ·Έκ° λͺ¨νΈνκ² μμλ JavaEE μ JakartaEE μ λν λ΄μ©μ΄λ spring security μ κ΄ν μ€μ μ μμΈν μμλ³Ό μ μμλ€. λ€μ ν¬μ€ν μμ μ€νλ§λΆνΈ3 μ graalVmμ κ΄ν λ΄μ©λ μΆκ°μ μΌλ‘ 곡λΆν΄λ΄μΌκ² λ€.