[Code Review] Spring Boot 영화 감상일지 프로젝트 리뷰: 아키텍처와 JPA 의존성 분리

2025. 12. 22. 14:52·Engineering/Backend Core

최근 다른 분이 작업하신 Spring Boot + JPA 기반의 영화 감상일지 REST API 프로젝트를 상세하게 코드 리뷰할 기회가 있었습니다.

🔗 관련 링크

  • GitHub Repository: https://github.com/heetwo2234/movie-diary-edit
  • 참고한 프로젝트: https://namgyus.tistory.com/8

기능적으로는 요구사항을 잘 충족하고 있었지만, **객체지향 설계 원칙(OOP)**과 유지보수성 관점에서 몇 가지 아쉬운 패턴들이 보였습니다. 이번 포스팅에서는 해당 프로젝트의 코드를 분석하며 발견한 장점과, 특히 Repository 설계에서 드러난 아키텍처 이해도의 아쉬움, 그리고 이를 어떻게 개선(Refactoring)해야 하는지를 중점적으로 다뤄보겠습니다.


1. 긍정적인 부분 (Good Points) 👏

먼저, 코드 전반에서 작성자의 꼼꼼함이 돋보이는 지점들이 있었습니다.

  • DTO(Data Transfer Object) 활용: 엔티티를 API 응답으로 직접 노출하지 않고 MemberDto.Response, PostDto.CreateRequest와 같이 철저하게 DTO로 분리하여 사용한 점은 매우 훌륭했습니다.
  • RESTful 설계 준수: /api/posts, /api/members 등 리소스 중심의 URI 설계와 HTTP 메서드 활용이 적절했습니다.
  • 명확한 예외 처리: DuplicateEmailException 등 커스텀 예외를 정의하여 비즈니스 로직의 흐름을 명확히 하려는 시도가 좋았습니다.

2. 핵심 리뷰: JPA 동작 원리 이해 부족 및 JPQL 활용의 부족  (아쉬운 점) 🛠️

이번 리뷰에서 가장 눈에 띄었던, 그리고 아키텍처에 대한 깊은 이해가 부족했다고 판단되는 부분은 바로 Repository 계층의 설계입니다.

❌ AS-IS: 현재 코드의 문제점

이번 리뷰에서 가장 아쉬웠던 점은 Spring Data JPA가 제공하는 편의 기능에만 지나치게 의존하고 있다는 점입니다.

❌ AS-IS: 현재 코드의 문제점 현재 MemberRepository를 포함한 대부분의 리포지토리가 JpaRepository를 상속받아, 메서드 이름 규칙(Query Method)이나 기본 제공 메서드에만 의존하여 구현되어 있습니다.

Java
// MemberRepository.java (현재 코드)
package com.kh.project.repository;

import com.kh.project.entity.Member;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface MemberRepository extends JpaRepository<Member, Long> {
    // 메서드 이름으로 쿼리를 생성하는 방식에만 의존
    Optional<Member> findByEmail(String email); 
}

🔍 Reviewer's Note: 이 코드는 기능적으로는 잘 동작하지만, "JPA가 실제로 데이터베이스와 어떻게 소통하는지"에 대한 고민이 생략되어 있습니다. 강사님께서도 늘 강조하시듯, JpaRepository를 상속받아 뚝딱 기능을 구현하는 것은 쉽지만, 이는 SQL이 어떻게 생성되고 실행되는지 개발자가 제어하지 못하게 만듭니다. 특히 실무에서 복잡한 데이터를 조회하거나 성능 최적화가 필요할 때, 단순 인터페이스 상속만으로는 한계에 부딪히게 됩니다.

개발자는 **JPQL(Java Persistence Query Language)**을 직접 작성함으로써 엔티티 객체를 대상으로 하는 쿼리가 실제로 어떻게 나가는지 명확히 이해하고 있어야 합니다.

✅ TO-BE: 개선 제안 (JPQL 활용) 단순히 프레임워크가 대신 만들어주는 쿼리에 의존하기보다, @Query 어노테이션을 사용하여 명시적인 JPQL을 작성하는 방향으로 리팩토링이 필요합니다.

Java
// MemberRepository.java (개선 제안)
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

@Repository
public interface MemberRepository extends JpaRepository<Member, Long> {

    // 명시적인 JPQL 작성: 개발자가 쿼리의 의도와 성능을 제어함
    @Query("select m from Member m where m.email = :email")
    Optional<Member> findByEmail(@Param("email") String email);
}

이렇게 JPQL을 직접 작성하면 다음과 같은 장점이 있습니다.

  1. 쿼리 주도권 확보: 불필요한 조인을 방지하거나, 원하는 데이터만 정확하게 조회(Projection)하는 등 쿼리를 최적화할 수 있습니다.
  2. 원리 이해: 객체지향 쿼리 언어가 SQL로 변환되는 과정을 이해하게 되어, 추후 N+1 문제 같은 성능 이슈에 더 유연하게 대처할 수 있습니다.
  3. 명확성: 메서드 이름이 길어지고 복잡해지는 것을 막고, 쿼리 로직을 한눈에 파악할 수 있습니다.

결론적으로, 현재 코드는 '구현'에는 성공했으나, JPA라는 기술의 본질인 JPQL과 영속성 컨텍스트를 다루는 능력을 보여주기에는 다소 아쉬움이 남습니다.


3. 추가 개선 제안 🔍

핵심적인 구조 문제 외에도 실무 레벨에서 고려해야 할 몇 가지 사항들을 코멘트했습니다.

1) Controller의 반환 타입 (Map 지양) MemberController의 getMemberStats 메서드를 보면 Map<String, Object>를 반환하고 있습니다.

Java
@GetMapping("/{id}/stats")
public ResponseEntity<Map<String, Object>> getMemberStats(...)

Map은 어떤 데이터가 들어있는지 명세가 불분명합니다. 협업하는 프론트엔드 개발자를 위해 MemberStatsResponse 같은 명확한 DTO를 정의하여 반환하는 것이 좋습니다.

2) 보안 취약점 (ID 검증 부재) 게시글 삭제 로직(PostController)을 보면 파라미터로 받은 memberId를 그대로 신뢰하여 권한을 체크하고 있습니다.

Java
@DeleteMapping("/{id}")
public ResponseEntity<Void> deletePost(@PathVariable Long id, @RequestParam Long memberId) {
    postService.deletePost(id, memberId);
}

이는 악의적인 사용자가 다른 사람의 memberId를 조작해서 보내면 타인의 글을 삭제할 수 있는 치명적인 구조입니다. JWT 토큰이나 세션에서 추출한 '현재 인증된 사용자 ID'와 비교하는 로직이 필수적입니다.

3) 검색 쿼리 성능 이슈 PostRepository의 searchByKeyword 메서드는 LIKE %keyword% 패턴을 사용합니다.

Java
@Query("SELECT p FROM Post p WHERE LOWER(p.title) LIKE ...")

데이터가 적을 땐 괜찮지만, 양방향 와일드카드는 인덱스를 타지 못해 데이터가 많아지면 성능 저하(Full Scan)가 발생합니다. 추후 QueryDSL 도입이나 Full Text Search 적용을 고려해볼 것을 권장했습니다.


📝 총평

이번 코드 리뷰를 통해 "프레임워크의 편의성"과 "좋은 아키텍처" 사이의 균형에 대해 다시 한번 생각해보게 되었습니다.

해당 코드는 기능 구현에는 충실했지만, Service 계층을 보호하기 위한 추상화 레벨에 대한 고민이 다소 부족했던 점이 아쉬웠습니다. 하지만 위에서 제안한 리팩토링 포인트들만 잘 적용한다면, 훨씬 더 견고하고 유지보수하기 좋은 프로젝트로 발전할 수 있을 것입니다.

'Engineering > Backend Core' 카테고리의 다른 글

[Code Review] Spring Boot 칵테일 프로젝트: JPA 설계의 모범 사례와 보안 취약점 개선  (0) 2025.12.15
[Spring Boot] 백엔드 핵심 흐름: JPA와 DDD, 그리고 AWS S3  (0) 2025.12.09
[Backend] DB 성능 최적화: 커넥션 풀(Connection Pool)과 HikariCP의 등장  (0) 2025.12.09
TIL: MVC 패턴의 본질과 Service 인터페이스의 진실  (0) 2025.12.09
코딩테스트 연습 - 홀짝 구분하기  (0) 2025.09.30
'Engineering/Backend Core' 카테고리의 다른 글
  • [Code Review] Spring Boot 칵테일 프로젝트: JPA 설계의 모범 사례와 보안 취약점 개선
  • [Spring Boot] 백엔드 핵심 흐름: JPA와 DDD, 그리고 AWS S3
  • [Backend] DB 성능 최적화: 커넥션 풀(Connection Pool)과 HikariCP의 등장
  • TIL: MVC 패턴의 본질과 Service 인터페이스의 진실
tlsgkstj
tlsgkstj
짱구의 성장 일기
  • tlsgkstj
    코딩하는 짱구
    tlsgkstj
  • 전체
    오늘
    어제
    • 분류 전체보기 (159)
      • About (1)
      • Projects (35)
        • Personal Projects (21)
        • Team Projects (14)
      • Engineering (20)
        • CS & Tools (0)
        • Backend Core (15)
        • Frontend (1)
        • Infra & Cloud (2)
        • AI & Tools (1)
      • Trouble Shooting & Issues (0)
      • Growth & Career (38)
        • Interview Prep (0)
        • Retrospectives (38)
      • Archive (65)
        • TIL (8)
        • Daily Dev Q&A (56)
  • 블로그 메뉴

    • 홈
    • About
    • Projects
    • Tech Stack
    • Dev Log
    • GitHub
  • 링크

    • github
  • 공지사항

  • 인기 글

  • 태그

    Project_Review
    til
    network
    프로덕트개발자
    backend
    jpa
    커리어리셋
    aws_s3
    REACT
    java
    프로젝트회고
    데브페스트
    클로드코드
    SpringBoot
    DevFestIncheon2025
    경기기후바이브코딩
    OrphanRemova
    Spring비교
    spring
    devlog
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
tlsgkstj
[Code Review] Spring Boot 영화 감상일지 프로젝트 리뷰: 아키텍처와 JPA 의존성 분리
상단으로

티스토리툴바