πŸš€ Spring Boot API λ¬Έμ„œν™” / Spring Security

Kim ChanwooΒ·2025λ…„ 9μ›” 24일

Spring Boot

λͺ©λ‘ 보기
3/6
post-thumbnail

πŸ“ RESTful API λ¬Έμ„œν™”

  • RESTful APIλŠ” λ‹€λ₯Έ κ°œλ°œμžκ°€ API κΈ°λŠ₯κ³Ό μž‘λ™ 방식을 μ‰½κ²Œ 이해할 수 μžˆλ„λ‘ λ¬Έμ„œν™”κ°€ ν•„μš”ν•©λ‹ˆλ‹€.

  • κ°œλ°œμžκ°€ 혼자 μ“°λ €κ³  λ§Œλ“€λ”λΌλ„ λ¬Έμ„œλŠ” ν•„μš”ν•©λ‹ˆλ‹€.

  • μ΄λ²ˆμ—λŠ” OpenAPI 3λ₯Ό μ΄μš©ν•˜μ—¬:

    • μ—”λ“œν¬μΈνŠΈ λͺ©λ‘
    • ν—ˆμš©λ˜λŠ” 데이터 ν˜•μ‹
    • API와 μƒν˜Έμž‘μš© 방법
      등을 포함할 μ˜ˆμ •μž…λ‹ˆλ‹€.

πŸ“Œ OpenAPI 3 기반 μžλ™ λ¬Έμ„œ 생성

  • μ΄μ „μ—λŠ” Swagger λͺ…세라고 ν–ˆμŠ΅λ‹ˆλ‹€.
  • ν•œκ΅­μ—μ„œλŠ” RAMLλ³΄λ‹€λŠ” OpenAPIλ₯Ό 더 많이 μ‚¬μš©ν•©λ‹ˆλ‹€.
  • Spring Bootμ—μ„œλŠ” SpringDoc OpenAPI Starter WebMVC UIλ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€.
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2'

πŸ’‘ μ™ΈλΆ€ 라이브러리 μ‚¬μš© μ‹œ build.gradle dependencies에 μΆ”κ°€κ°€ ν•„μš”ν•©λ‹ˆλ‹€.


πŸ”§ OpenAPI μ„€μ • 예제

com.example.cardatabase νŒ¨ν‚€μ§€μ— OpenApiConfig 클래슀 생성:

@Configuration
public class OpenApiConfig {
    @Bean
    public OpenAPI carDatabaseOpenApi() {
        return new OpenAPI()
                .info(new Info()
                        .title("Car REST API")
                        .description("My car Stock")
                        .version("1.0")
                );
    }
}
  • @Bean : 이 λ©”μ„œλ“œμ—μ„œ λ°˜ν™˜ν•˜λŠ” 객체λ₯Ό Spring IoC μ»¨ν…Œμ΄λ„ˆκ°€ κ΄€λ¦¬ν•˜λ„λ‘ 등둝
  • new Info() λ‚΄λΆ€: λΉŒλ” νŒ¨ν„΄κ³Ό λ©”μ„œλ“œ 체이닝 ν™œμš©

βš™ application.properties μ„€μ •

spring.application.name=cardatabase
spring.datasource.url=jdbc:mariadb://localhost:3310/cardb
spring.datasource.username=root
spring.datasource.password=1234
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=create-drop
spring.data.rest.basePath=/api
springdoc.swagger-ui.enabled=true
springdoc.swagger-ui.path=/swagger-ui.html
springdoc.api-docs.path=/api-docs

🟒 Spring Bean / @Bean

1️⃣ Beanμ΄λž€?

  • Spring IoC μ»¨ν…Œμ΄λ„ˆκ°€ κ΄€λ¦¬ν•˜λŠ” 객체
  • Spring 이전: κ°œλ°œμžκ°€ 직접 객체 생성 β†’ μ˜μ‘΄μ„± 관리 어렀움
  • Spring 이후: IoC μ»¨ν…Œμ΄λ„ˆμ— 객체 생성 μœ„μž„ β†’ μ‹±κΈ€ν†€μœΌλ‘œ 관리 κ°€λŠ₯

2️⃣ @Bean μ• λ„ˆν…Œμ΄μ…˜

  • λ©”μ„œλ“œμ— λΆ™μ—¬μ„œ λ°˜ν™˜ 객체λ₯Ό Spring μ»¨ν…Œμ΄λ„ˆμ— 등둝
  • 주둜 @Configuration 클래슀 μ•ˆμ—μ„œ μ‚¬μš©
@Configuration
public class OpenApiConfig {
    @Bean
    public OpenAPI carDatabaseOpenApi() {
        return new OpenAPI()
                .info(new Info()
                        .title("Car REST API")
                        .description("My car Stock")
                        .version("1.0")
                );
    }
}

3️⃣ @Bean μ‚¬μš© μ—¬λΆ€ 비ꡐ

μ‚¬μš©ν•˜μ§€ μ•Šμ„ λ•Œ

public class MyController {
    private MyService myService1 = new MyService();

    public void handleRequest() {
        myService1.doSomething();
    }
}
  • 단점: 객체 κ°„ 결합도 ↑, ν…ŒμŠ€νŠΈ 어렀움, μœ μ§€λ³΄μˆ˜ λΆ€λ‹΄

μ‚¬μš©ν–ˆμ„ λ•Œ

@Configuration
public class AppConfig {
    @Bean
    public MyService myService() {
        return new MyService();
    }
}

@Controller
public class MyController {
    private final MyService myService;

    public MyController(MyService myService) {
        this.myService = myService;
    }

    public void handleRequest() {
        myService.doSomething();
    }
}
  • μž₯점: DI 적용, 객체 싱글톀 관리, 결합도 ↓, ν…ŒμŠ€νŠΈ 용이

πŸ” λ°±μ—”λ“œ 보호

1️⃣ Spring Security κ°œμš”

  • κΈ°λ³Έ κΈ°λŠ₯

    1. 인메λͺ¨λ¦¬ μ‚¬μš©μž 생성 (user / μ½˜μ†” λΉ„λ°€λ²ˆν˜Έ)
    2. /css, /images λ“± 정적 λ¦¬μ†ŒμŠ€ μ ‘κ·Ό ν—ˆμš©
    3. 기본 인증(HTTP Basic) 적용
    4. HSTS, XSS, CSRF λ“± λ³΄μ•ˆ κΈ°λŠ₯ ν™œμ„±ν™”
    5. κΈ°λ³Έ 둜그인 νŽ˜μ΄μ§€ 제곡

λ„μž… 방법

implementation 'org.springframework.boot:spring-boot-starter-security'
testImplementation 'org.springframework.boot:spring-boot-starter-security'
  • Spring Security 적용 ν›„ /login νŽ˜μ΄μ§€ μžλ™ 생성
  • ν…ŒμŠ€νŠΈμš© 인메λͺ¨λ¦¬ μ‚¬μš©μž 제곡

2️⃣ SecurityConfig 예제

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public InMemoryUserDetailsManager userDetailsService() {
        UserDetails user = User.builder()
                .username("user")
                .password(passwordEncoder().encode("password"))
                .roles("USER")
                .build();
        return new InMemoryUserDetailsManager(user);
    }

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
}
  • @EnableWebSecurity : Spring Security κΈ°λ³Έ 보호 ꡬ성 ν•΄μ œ β†’ μ»€μŠ€ν…€ κ°€λŠ₯
  • PasswordEncoder : λΉ„λ°€λ²ˆν˜Έ μ•”ν˜Έν™” (bcrypt)

3️⃣ DB 기반 μ‚¬μš©μž 인증

  1. μ—”ν‹°ν‹° 클래슀 생성 (AppUser)
@Entity
@NoArgsConstructor(force = true)
@RequiredArgsConstructor
@Getter
@Setter
public class AppUser {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(nullable=false,unique=true)
    private final String username;

    @Column(nullable=false)
    private final String password;

    @Column(nullable=false)
    private final String role;
}
  1. Repository 생성
@RepositoryRestResource(exported = false)
public interface AppUserRepository extends JpaRepository<AppUser, Long> {
    Optional<AppUser> findByUsername(String username);
}
  • Optional : null 처리 μ•ˆμ „ν•˜κ²Œ κ°€λŠ₯
  • @RepositoryRestResource(exported = false) : REST μžλ™ λ…ΈμΆœ λ°©μ§€
  1. UserDetailsService κ΅¬ν˜„
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    private final AppUserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Optional<AppUser> user = userRepository.findByUsername(username);
        if (user.isPresent()) {
            AppUser currentUser = user.get();
            return User.withUsername(username)
                       .password(currentUser.getPassword())
                       .roles(currentUser.getRole())
                       .build();
        } else {
            throw new UsernameNotFoundException("User not found");
        }
    }
}
  • DBμ—μ„œ μ‚¬μš©μžλ₯Ό 가져와 Spring Security 인증에 맞게 λ³€ν™˜
  • UserBuilder ν™œμš©, μΈν„°νŽ˜μ΄μŠ€ κ°•μ œ κ΅¬ν˜„
  1. SecurityConfig μˆ˜μ • (DB 연동)
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    private final UserDetailsServiceImpl userDetailsService;

    public SecurityConfig(UserDetailsServiceImpl userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    public void configGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService)
            .passwordEncoder(new BCryptPasswordEncoder());
    }

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
}
  • 인메λͺ¨λ¦¬ μ‚¬μš©μž β†’ DB 기반 μ‚¬μš©μžλ‘œ λ³€κ²½
  • 둜그인 μ‹œ AppUser ν…Œμ΄λΈ” 정보 μ‚¬μš©

πŸ”‘ Postman ν…ŒμŠ€νŠΈ

  • GET /api/cars β†’ 인증 ν•„μš” β†’ 401 Unauthorized
  • POST μš”μ²­ μ‹œ Authorization νƒ­ β†’ Basic Auth에 username/password μž…λ ₯
  • AppUser ν…Œμ΄λΈ” 확인 κ°€λŠ₯ (id, username, μ•”ν˜Έν™”λœ password)

πŸ’‘ 정리 포인트

  1. OpenAPI 3 β†’ RESTful API μžλ™ λ¬Έμ„œν™”
  2. @Bean / Spring IoC β†’ 객체 관리 및 DI
  3. Spring Security β†’ 인메λͺ¨λ¦¬ β†’ DB 연동 인증
  4. RepositoryRestResource β†’ REST λ…ΈμΆœ μ œμ–΄

0개의 λŒ“κΈ€