Spring Boot 70 파일 와일드카드 import 제거 + Spotless · 거대 메서드 분해 — POP-SPOT v1.4 백엔드 클린코드
백엔드 7 Wave · 70 파일 · 약 3,700 라인 정리. Spotless 자동 포맷, 와일드카드 import 0, 매직 넘버 상수화, 130 줄 메서드 6 단계로. API/DB 동등.
기능은 그대로 두고 백엔드 코드만 정리한 버전이다. 외부 동작 100% 동일. 70 파일 · 약 3,700 라인. API 응답·DB 스키마·Redis 키 그대로 유지.
이 글에서 다루는 것
모르는 단어 한 줄로
| 용어 | 한 줄 설명 |
|---|---|
| Spotless | Gradle 플러그인. 코드 포맷을 고정시켜 빌드 시 다른 게 있으면 실패 |
| googleJavaFormat AOSP | 구글에서 만든 자바 코드 포맷 규칙. AOSP 변형은 4 칸 인덱트·100 컬럼 제한 |
| 와일드카드 import | <code>import java.util.*;</code> 처럼 패키지 전체를 가져오는 구문. 이름 충돌 이 일어나면 추적이 어렵다 |
| 매직 넘버 | "이게 왜 5 인가" 를 설명해주는 이름 없이 코드에 박힌 숫자 |
| SLF4J | 로그 출력의 표준 인터페이스. <code>System.out.println</code> 보다 제어가 쉬움 |
무엇이 바뀌었나
Wave 단위로 끊어서 진행하면 "어디까지 촔겁는가" 에 진행 흐름을 잡을 수 있음.
| Wave | 범위 | 핵심 작업 |
|---|---|---|
| 1 | build.gradle | Spotless 플러그인 삽입, googleJavaFormat AOSP 적용 |
| 2 | 음악 서비스 7 파일 | SpotifySearchService 5 단계 폴백 분해, 매직 넘버 추출 |
| 3 | 자동수집 크롤러 8 파일 | PopupCrawlOrchestrator 130 줄 → 6 단계 분해 |
| 4 | Controller 25 파일 | 와일드카드 import 0, 헬퍼 메서드 분리 |
| 5 | Service 16 파일 | AuthService/OrderService 7 단계 분해, 메서드 평균 30 줄 미만 |
| 6 | Entity 핵심 6 | User · PopupStore · MatePost · Stamp · Orders · MyCourse |
| 7 | Config/Exception 9 | SecurityConfig · JwtFilter · WebSocketConfig · RateLimit |
| 보강 | DTO 15 + 잔여 Entity 7 | "⚡ [13번 수정]" 같은 편집 흔적 84 건 제거 |
결과: API 응답 동일, DB 스키마 동일, Redis 키 동일. 외부에서 볼 때는 아무것도 바뀌지 않은 것으로 보이는 리팩터링.
왜 이렇게 했음
Spotless 자동 포맷 — 포트폴리오 용 코드 퀄리티 향상이 목표. 사람 눈으로 "탭·스페이스 통일 되어 있나" 확인하는 건 한계가 있고 롬복으로 놈치기도 쉬운다. Spotless 를 빌드 파이프라인에 넣으면 "고치기 전엔 다음 머지 없음" 으로 결정된다.
와일드카드 import 제거 — IDE 가 자동으로 * 로 접어주는 경우가 많은데, 나중에 이름 충돌 문제 (User 도메인 클래스 vs User 타입 vs lucide-react User 아이콘)이 생기면 추적이 어렵다. 구체적으로 적으면 IDE 의 재명명/찾기 기능이 훨씬 정확해진다.
매직 넘버 상수화 — 5 가 "로그인 최대 시도 횟수" 인지 "리트라이 지연 초" 인지 읽는 사람은 모른다. MAX_LOGIN_ATTEMPTS_PER_MINUTE = 5 로 적으면 몇 초간 고민할 필요가 없을 정도로 명시적.
130 줄 메서드 분해 — runOnce() 안에 검색 · 정규화 · 중복 제거 · Geocoding · 저장 · 로그기록 이 전부 섞여 있었다. 명명된 단계로 나누면 구조가 문단 수준에서 읽힌다.
코드로는 어떻게 (필요한 부분만)
build.gradle 에 Spotless 추가.
파일: popspot-backend/build.gradle (Gradle 빌드 설정)
// v1.4 — popspot-backend/build.gradle 에 Spotless 도입
plugins {
id 'com.diffplug.spotless' version '6.25.0'
// ^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^
// Gradle 플러그인 ID 버전 고정 — 파이프라인 재현성 확보
}
spotless {
java {
// ^^^^ java 소스셋에만 규칙 적용. groovy/kotlin 에도 각각 설정 가능
googleJavaFormat('1.22.0').aosp()
// ^^^^^^^^^^^^^^^^ ^^^^^^^^ ^^^^^^
// | | |
// | | +-- AOSP 변형: 4칸 인덱트 (기본은 2칸)
// | | 100 컸럼 한계 — 개인 노트북에서 한 눈에 들어오는 폭
// | +-- google-java-format 버전 (포맷 규칙 버전도 고정)
// +-- Google 의 공식 자바 포맷. PR 마다 "탭/스페이스 다르다" 싸움 종결
removeUnusedImports()
// ^^^^^^^^^^^^^^^^^^^^^ 실제로 안 쓰는 import 자동 제거
// IDE 의 "unused import warning" 에만 의존하지 않아도 됨
endWithNewline()
// ^^^^^^^^^^^^^^^^ 파일 끝에 개행문자 강제 — POSIX 표준. git diff 노이즈 제거
}
}
// v1.4 — ./gradlew check 시 spotlessCheck 가 먼저 돌아감
tasks.named('check') { dependsOn 'spotlessCheck' }
// ^^^^^^^ ^^^^^^^^^^^^^
// | |
// | +-- 포맷 위반 있으면 빌드 실패 → PR 머지 차단
// +-- gradle 표준 검증 태스크. CI 에서 자동 호출됨
// 수정 하려면 ./gradlew spotlessApply 실행 → 그림 자동 수정매직 넘버 상수화.
파일: popspot-backend/src/main/java/com/popspot/service/AuthService.java (로그인 시도 회수 체크)
// v1.3 까지 — 매직 넘버 그대로 노출
if (attempts >= 5) lockAccount();
// ^ 이 5 가 무슨 뜻인지 다른 개발자가 알 방법 없음
// - 로그인 최대 시도 5회?
// - 재시도 지연 5초?
// - 고정 세션 5분?
// 고치고 싶을 때 grep 으로 '5' 찾으면 수백 결과파일: 같은 AuthService.java (v1.4 교체 후) + 상수 자체는 같은 클래스 상단에 선언
// v1.4 — 이름 있는 상수로 추출
private static final int MAX_LOGIN_ATTEMPTS_PER_MINUTE = 5;
// ^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// | |
// | +-- 대문자 스네이크 — 자바 관습상 상수와 필드 구분
// +-- final 은 JVM 이 관련 최적화 적용. static 은 인스턴스당 복제 안 됨
if (attempts >= MAX_LOGIN_ATTEMPTS_PER_MINUTE) lockAccount();
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 읽으면서 의미 해석이 같이 일어남
// 이제 조건을 읽으면 입력 값과 의도가 한꺼번에 드러남130 줄 메서드 분해.
파일: popspot-backend/src/main/java/com/popspot/service/crawler/PopupCrawlOrchestrator.java (v1.3 양식)
// v1.3 까지 — runOnce() 한 메서드 안에 단계가 전부 섬여 있음 (약 130줄)
public void runOnce() {
// 검색 호출 ... 30 줄 ← Naver/Kakao Search API 둘을 순차 호출
// 정규화 ... 25 줄 ← Groq 프롬프트 컴파일 후 답변 JSON 파싱
// 중복 제거 ... 15 줄 ← 제목·주소·날짜 같은 건 하나로
// Geocoding ... 20 줄 ← 주소를 lat/lng 로 변환 (Kakao Local API)
// DB 저장 ... 25 줄 ← confidence 계산 + 조건 충족 시 INSERT
// 로그 ... 15 줄 ← 로그 기록·메트릭 업데이트
// 문제: 한 단계가 실패했을 때 제쳐두고 다음 단계로 가기 어려움 (조기 탈출 return 이 불편)
}파일: 같은 PopupCrawlOrchestrator.java (v1.4 분해 후)
// v1.4 — 명명된 6개 단계로 분해. 메서드 1개 → 7개
public void runOnce() {
var rawResults = collectRawSnippets();
// ^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^
// | | |
// | | +-- 1단계: 원시 검색 결과 수집 (List<RawSnippet>)
// | +-- 단계 이름이 변수명으로 이어짐 → 디버깅 시 변수 값을 보면 단계 의미가 바로 드러남
// +-- Java 10+ var. 타입 추론 — 점용 타입을 적아 가독성 우선
var normalized = normalize(rawResults);
// ^^^^^^^^^^ 2단계: 앞 단계 결과 그대로 넘김
// Groq 정규화. List<NormalizedPopup>
var deduplicated = removeDuplicates(normalized);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 3단계: 해시(title+address+date) 기준 중복 제거
var withCoordinates = geocode(deduplicated);
// ^^^^^^^^^^^^^^^^^^^^^ 4단계: Kakao Local 로 lat/lng 채움
var saved = persistWithConfidence(withCoordinates);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 5단계: confidence ≥0.8 이면 게시
logRunSummary(saved);
// ^^^^^^^^^^^^^ 6단계: 수집 요약을 admin 로그로 남김
}
// 결과: 한 단계만 따로 테스트 가능·단계 당 평균 25줄 안판·각 메서드 IDE outline 에서 본문처럼 읽힘핵심 파일: popspot-backend/build.gradle, popspot-backend/src/main/java/.../service/crawler/PopupCrawlOrchestrator.java
비하인드 · 사고
Spotless 최초 실행하자 109 파일이 자동 수정되었다. 사소한 게 아니라 JavaDoc 의 긴 줄을 100 컬럼 기준으로 자동 줄바꿈해서 일어난 건데, 마침 한국어 멀티라인 주석이 많이 걸렸다. 이 문제는 v2.7.1, v2.7.2, v2.10.1 에서 다시 돌아온다.
교훈 한 줄. 자동 포맷은 설정을 한 번 맞추면 운영이 편하지만, 입력 규칙 (멀티라인 주석 최대 컬럼) 을 처음에 단단하게 맞춰둘면 다음 번 충돌이 적다.