Java Spring 클린 코드 리팩토링 모음집
코드 품질, 유지보수성, 협업 효율까지 한 번에 높이는 실전 리팩토링 팁!
리팩토링 배경
진행 중인 Spring 기반 프로젝트에서 API 응답/서비스 로직/예외 처리 등 여러 부분에서 반복 로직, 모호한 네이밍, 경고 메시지 등이 발견되어 전체적인 클린 코드 리팩토링을 진행하게 되었습니다.
특히 도메인 서비스에 많은 책임이 몰려 있는 구조에서 다음과 같은 목표를 설정했습니다:
- 도메인 분리 및 패키지 구조 정리
- 불필요한 의존성 제거
- 일관성 있는 RESTful 설계
- 네이밍 컨벤션 통일
- 정적 분석 도구(SonarLint 등)의 경고 제거
리팩토링 항목별 정리
1. Raw Type → 제네릭 명시
Before
public CommonResponse handleCommentNotFoundException(CommentNotFoundException e)
After
public CommonResponse<Void> handleCommentNotFoundException(CommentNotFoundException e)
Raw types should not be used 경고 제거. Void는 결과 값이 없을 때 사용.
왜 유리한가?
- 타입 안정성 확보: 제네릭을 명시함으로써 컴파일 타임에 타입 오류를 방지할 수 있음
- 가독성 향상: 반환 타입이 명확해짐
- 협업 시 함수의 의도가 분명해져, 다른 개발자가 의심 없이 사용할 수 있음
2. @PathVariable 경로와 메서드 시그니처 일치
Before
@PatchMapping("/{postId}/comments/{commentId}")
public CommonResponse<Void> updateComment(@PathVariable Long commentId, ...)
After (1) – 경로 유지 시
@PatchMapping("/{postId}/comments/{commentId}")
public CommonResponse<Void> updateComment(@PathVariable Long postId, @PathVariable Long commentId, ...)
After (2) – 경로 단순화 시
@PatchMapping("/comments/{commentId}")
public CommonResponse<Void> updateComment(@PathVariable Long commentId, ...)
URL에 있는 PathVariable은 반드시 메서드에 선언해야 함.
Restful한 상태를 유지하기 위해서 postId를 유지하는 방식을 채택했다.
왜 유리한가?
- 스프링 경고 방지 및 예상 가능한 동작 보장
- API 문서화 시 path parameter 누락 없이 반영 가능
- 불필요한 path 파라미터 제거로 테스트 코드 및 유효성 검사 코드 단순화 가능
3. 유틸 클래스는 private 생성자
Before
public final class S3Image {
public static final String DEFAULT_URL = "1";
}
After
public final class S3Image {
public static final String DEFAULT_URL = "1";
private S3Image() {}
}
유틸 클래스 인스턴스화 방지.
왜 유리한가?
- 객체 생성 방지로 메모리 낭비 최소화
- 의도하지 않은 사용을 원천 차단함으로써 코드 신뢰도 향상
- 클래스의 용도를 명확히 하여 유지보수성 강화
4. EnumMap 사용 권장
Before
Map<BoardType, List<Post>> map = new HashMap<>();
After
Map<BoardType, List<Post>> map = new EnumMap<>(BoardType.class);
Maps with enum keys should use EnumMap 경고 해결.
왜 유리한가?
- EnumMap은 HashMap보다 성능과 메모리 효율성이 뛰어남
- enum key의 순서를 유지하므로 안정적인 결과 예측 가능
- 키 충돌 위험이 없고, Enum의 의미를 정확히 표현 가능
5. 중복 문자열은 상수화
Before
return new CommentResponse(null, "deleted", "삭제된 댓글입니다.", ...);
After
public class CommentConstants {
public static final String DELETED = "deleted";
public static final String DELETED_MESSAGE = "삭제된 댓글입니다.";
}
하드코딩 방지, 재사용성 향상.
왜 유리한가?
- 다국어 처리, 메시지 교체 시 단일 변경만으로 전체 코드 반영 가능
- 협업 시 동일한 상수로 통일해 사용 가능 → 중복 및 실수 방지
6. Stream 수집 시 .toList() 사용 (JDK 16+)
Before
list.stream().map(...).collect(Collectors.toList());
After
list.stream().map(...).toList();
Stream.toList()는 불변 리스트를 반환.
왜 유리한가?
- 가독성 향상 및 코드 간결화
- 불변 리스트를 통해 불필요한 수정 방지 → 예측 가능한 코드 작성 가능
- 최신 Java 기능 적극 사용 → 트렌드 반영 및 유지보수 편리
7. 불필요한 지역 변수 제거
Before
List<Item> items = repository.findAll();
return items;
After
return repository.findAll();
Local variables should not be declared and immediately returned 경고 제거.
왜 유리한가?
- 코드의 목적이 더 명확하게 드러남
- 필요하지 않은 이름 공간 낭비 방지
- 코드 라인이 줄어들어 전반적 가독성 향상
8. Null dereference 방지
Before
String safe = Jsoup.clean(request.content(), Safelist.none());
After
String raw = Optional.ofNullable(request.content()).orElse("");
String safe = Jsoup.clean(raw, Safelist.none());
NPE 예방.
왜 유리한가?
- 런타임 예외 예방 → 안정성 향상
- Optional 사용을 통해 null-safe 패턴을 코드 전반에 적용 가능
- 테스트 시 null 케이스 대응이 간편
✍️ 마무리
"작동하는 코드보다 이해하기 쉬운 코드가 더 좋다"는 말처럼,
소소한 리팩토링 하나하나가 장기적으로 프로젝트를 얼마나 안정적으로 만드는지를 직접 체감했습니다.
'CS공부 > Java & Spring' 카테고리의 다른 글
| Spring 내부 구조 - DispatcherServlet, 요청 흐름, 에러 처리 (0) | 2025.04.22 |
|---|---|
| Spring Boot @Transactional 어노테이션 (0) | 2025.04.22 |
| 힙 메모리 누수 (0) | 2025.04.22 |
| 가비지 컬렉션(Garbage Collection) (0) | 2025.04.21 |
| JVM과 힙, 스택 (0) | 2025.04.21 |