[Discodeit] 스프링부트 생성 직후 무엇을 먼저 할까

Jerry·2025년 8월 13일

Discodeit 프로젝트

목록 보기
1/1

1. 우선 실행 + 헬스체크

  • ./gradlew bootRun으로 기본 실행 확인
  • spring-boot-starter-actuator 추가 → /actuator/health로 기동성 확인

./gradlew bootRun이란?

Gradle의 Spring Boot 플러그인이 제공하는 실행 태스크입니다.
소스코드를 컴파일하고(리소스 포함) 메인 클래스를 JVM에서 바로 띄웁니다. JAR로 패키징하지 않아도 개발 중 즉시 실행할 수 있습니다.

  • 기본 메인 클래스 감지는 @SpringBootApplication이 붙은 클래스 기준
  • 실행 시 클래스패스: build/classes/java/main + build/resources/main + 의존 라이브러리

bootRun에서 자주 쓰는 옵션들

애플리케이션 인자(Program args)

Spring Boot는 --key=value 형식을 인식합니다.

./gradlew bootRun --args='--spring.profiles.active=dev --server.port=8081'
./gradlew bootRun --args='--logging.level.root=debug'

JVM 인자

메모리, 모듈 오픈, 디버깅 포트 등은 JVM 인자에 넣습니다.

# 한 번에 전달
./gradlew -Dspring-boot.run.jvmArguments="-Xms512m -Xmx1g" bootRun

# build.gradle.kts에 고정
tasks.bootRun {
    jvmArgs("-Xms512m", "-Xmx1g")
}
  • Xms512m : JVM 힙(Heap)의 시작/최소 크기를 512MiB로 설정
  • Xmx1g : JVM 힙의 최대 크기를 1GiB(=1024MiB)로 제한

프로필/환경변수

# 환경변수로
SPRING_PROFILES_ACTIVE=prod ./gradlew bootRun
# 또는 프로그램 인자로
./gradlew bootRun --args='--spring.profiles.active=prod'
# 또는 시스템 프로퍼티로
./gradlew -Dspring-boot.run.profiles=prod bootRun

디버깅

# Gradle의 JavaExec 디버그(디폴트 5005, suspend=Y)
./gradlew bootRun --debug-jvm

# 혹은 JVM 인자 직접 지정(서버 모드, 비대기)
./gradlew -Dspring-boot.run.jvmArguments="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005" bootRun

로그/스택 출력(Gradle 레벨)

./gradlew bootRun --info
./gradlew bootRun --stacktrace

핫리로드(개발 편의)

  • 의존성에 spring-boot-devtools 추가
  • bootRun 실행 + 코드 저장 → DevTools가 자동 재시작
  • 빌드 자동화를 곁들이고 싶으면 다른 터미널에서:
./gradlew -t classes   # 변경 감지하여 지속 컴파일

참고: Windows에선 gradlew.bat를 사용하세요.

IntelliJ 핫리로드

파일 저장 → 자동 컴파일 → DevTools가 재시작되게 세팅합니다.

  1. File > Settings > Build, Execution, Deployment > Compiler
    • Build project automatically
  2. File > Settings > Advanced Settings
    • Allow auto-make to start even if the developed application is currently running
  3. (선택) Build, Execution, Deployment > Build Tools > Gradle
    • “Build and run using”을 Gradle로 위임하면 CI와 동일한 흐름,
      IntelliJ로 두면 더 빠른 편(대신 CI와 차이가 날 수 있음). 둘 중 하나로 고정하세요.

이제 Run(또는 Debug)로 띄운 뒤 코드 저장만 해도 자동 빌드→재시작이 됩니다.

2. 프로필 분리와 설정 골격

  • application.yml (공통) + -dev.yml, -test.yml, -prod.yml로 환경 분리
  • 공통 기본값과 로깅/타임존/Jackson을 먼저 고정
spring:
  profiles:
    default: dev

server:
  shutdown: graceful
  compression:
    enabled: true
# application-dev.yml
spring:
  autoconfigure:
    exclude:
      - org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
      - org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
      - org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration

  devtools:
    restart:
      # 파일 저장소 변경에 따른 재시작 루프 방지
      additional-exclude: |
        file-data-map/**,
        **/*.ser

management:
  endpoint:
    health:
      show-details: always

logging:
  level:
    com.sprint.mission.discodeit: DEBUG

app:
  storage:
    type: file
    root-dir: file-data-map

3. 타입 세이프 구성 바인딩

  • 환경변수를 코드에 흩뿌리지 말고 @ConfigurationProperties로 묶습니다.
@ConfigurationProperties(prefix = "app")
public record AppProperties(String name, String version) {}

@EnableConfigurationProperties(AppProperties.class)
@SpringBootApplication
class Application {}
app:
  name: Discodeit
  version: 1.0.0

4. 코드 스타일 & 품질 가드 설치

  • .editorconfig(인덴트, 줄바꿈), Spotless + Checkstyle(google style 수정본), Lombok 설정 고정
  • 첫 커밋 전에 포맷터를 CI와 로컬에 동일하게 맞춥니다.
plugins { id "com.diffplug.spotless" version "6.25.0" }
spotless {
  java { googleJavaFormat().reflowLongStrings()
         target 'src/**/*.java' }
}

5. 전역 예외 & 검증 틀

  • 컨트롤러마다 try/catch 금지. @RestControllerAdvice로 모으고, Bean Validation 기본 세팅
@RestControllerAdvice
class GlobalExceptionHandler {
  @ExceptionHandler(MethodArgumentNotValidException.class)
  ResponseEntity<ApiError> handleValidation(MethodArgumentNotValidException e) {
    var msg = e.getBindingResult().getFieldErrors().stream()
        .map(err -> err.getField() + ": " + err.getDefaultMessage())
        .toList();
    return ResponseEntity.badRequest().body(new ApiError("VALIDATION_ERROR", msg));
  }
  record ApiError(String code, Object message) {}
}
  • DTO에 @NotBlank @Email @Size 등을 바로 붙여 입력 경계부터 견고하게.

6. 보안 뼈대 & CORS

  • 나중에 도입해도 되지만 뼈대만 지금 잡아두면 안전합니다.
implementation "org.springframework.boot:spring-boot-starter-security"
@Configuration
class SecurityConfig {
  @Bean SecurityFilterChain filter(HttpSecurity http) throws Exception {
    http.csrf(csrf -> csrf.disable())
       .cors(Customizer.withDefaults())
       .authorizeHttpRequests(auth -> auth
         .requestMatchers("/actuator/**","/api/v1/ping").permitAll()
         .anyRequest().authenticated())
       .httpBasic(Customizer.withDefaults());
    return http.build();
  }
  @Bean CorsConfigurationSource cors() {
    var c = new CorsConfiguration();
    c.setAllowedOrigins(List.of("http://localhost:3000"));
    c.setAllowedMethods(List.of("GET","POST","PUT","PATCH","DELETE"));
    c.setAllowedHeaders(List.of("*"));
    var s = new UrlBasedCorsConfigurationSource();
    s.registerCorsConfiguration("/**", c); return s;
  }
}

7. 관측 가능성(Observability) 기본값

  • Actuator 설치 시점에 로그 패턴과 트레이스 아이디 준비
logging.pattern.level: "%5p [%X{traceId:-} %X{spanId:-}]"
management.tracing.sampling.probability: 1.0   # 필요시 Micrometer Tracing

8. DB 마이그레이션 도구(Flyway/Liquibase) 선택

  • 초기에 Flyway를 넣어 두면 테이블이 늘어나도 배포가 안정적입니다.
implementation "org.flywaydb:flyway-core"
src/main/resources/db/migration/V1__init.sql

9. 테스트 전략을 ‘처음 기능’과 함께 확정

  • JUnit5 + AssertJ + Spring Boot Test + (선택) Testcontainers
  • 첫 수직 슬라이스(예: /users의 POST/GET) 구현과 동시에
    • 서비스 단위 테스트
    • 컨트롤러 슬라이스(@WebMvcTest) 검증
    • 간단한 통합 테스트(@SpringBootTest(webEnvironment=RANDOM_PORT))

10. 첫 수직 슬라이스(Vertical Slice) 구현

  • DTO ↔ Service ↔ Repository를 얇게 통과하는 최소 기능 1개를 끝까지 완료
    (예: POST /users → 입력 검증 → 저장 → 응답 매핑 → 예외/로그/테스트까지)
  • 이 슬라이스가 이후 모든 기능의 템플릿이 됩니다
profile
Backend engineer

0개의 댓글