Post

김성훈님 포트폴리오 최종 작업 내용 피드백

메가스터디IT아카데미 SpringBoot 백엔드 개발자 과정 주말반 (25.02.22 ~ 25.09.13). 김성훈님의 포트폴리오 4주차 작업 내용에 대한 피드백

김성훈님 포트폴리오 최종 작업 내용 피드백

김성훈님 프로젝트 피드백

안녕하세요! 한 달간 진행한 Spring Boot 프로젝트를 리뷰해 본 결과, 전반적으로 수업 내용과는 다른 방향으로 자신만의 기능을 구현하려는 시도가 인상적이었습니다.

아래 피드백을 통해 잘된 점은 더욱 발전시키고, 개선이 필요한 부분은 수업 예제 코드와 비교하며 보완하여 한 단계 더 성장하는 계기가 되기를 바랍니다.

1. 잘된 점 (Good Points)

1.1. Flyway를 이용한 체계적인 DB 스키마 관리

  • 관련 파일: build.gradle, src/main/resources/db/migration/

build.gradleflyway 의존성을 추가하고, V1__..., V2__... 와 같은 SQL 스크립트를 통해 데이터베이스 스키마 변경 내역을 체계적으로 관리한 점은 긍정적인 부분입니다. 이는 수업 예제에는 없던 내용으로, 직접 학습하여 적용한 것으로 보입니다. 수업 내용과 다른 방향성이기 때문에 장점으로 평가되지만 특별히 피드백할 부분은 없습니다.

1.2. 인터셉터를 활용한 공통 기능 구현

  • 관련 파일: src/main/java/com/roboai/mvc/web/RequestLoggingInterceptor.java

모든 API 요청에 대한 정보를 기록하기 위해 HandlerInterceptor를 구현한 것은 좋은 접근 방식입니다. preHandleafterCompletion을 적절히 사용하여 요청 시작부터 완료까지의 흐름을 정확히 추적하고, IP 주소, User-Agent, 처리 시간 등 의미 있는 데이터를 수집하는 로직을 꼼꼼하게 작성했습니다. 덕분에 컨트롤러 코드의 중복을 피하고 공통 기능을 깔끔하게 분리할 수 있었습니다.

수업 예제 코드 중에서 MyInterceptor와 유사한 역할을 하는 부분이 있으니, 그 구조와 패턴을 참고하여 더욱 견고하게 다듬어 보시길 권장합니다.

1.3. 명확한 패키지 구조와 책임 분리

  • 관련 파일: src/main/java/com/roboai/mvc/

api, controller, service, repository, model, config, web 등 역할에 따라 패키지를 명확하게 분리한 점이 좋습니다. 이는 코드의 가독성을 높이고 유지보수를 용이하게 만드는 좋은 습관입니다.

2. 개선점 (Areas for Improvement)

2.1. [반드시 수정] 데이터베이스 접근 기술의 차이 (JPA vs MyBatis)

  • 문제점: 수업 예제에서는 MyBatis를 사용하여 데이터베이스와 연동했지만, 학생의 프로젝트는 Spring Data JPA를 사용하고 있습니다.
  • 관련 파일: build.gradle, ApiAuditLogRepository.java

build.gradle (L19)

1
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

ApiAuditLogRepository.java (L12)

1
public interface ApiAuditLogRepository extends JpaRepository<ApiAuditLog, Long> {}

수업과는 다른 방향성: 수업의 핵심 목표 중 하나는 MyBatis의 동작 방식과 Mapper를 활용한 SQL 관리 방법을 깊이 있게 이해하는 것입니다. JPA는 매우 훌륭하고 편리한 기술이지만, 현재 단계에서는 수업에서 배운 내용을 충분히 숙달하는 것이 더 중요합니다. 의도적으로 다른 기술을 사용한 것이라면 그 이유와 장단점을 명확히 설명할 수 있어야 합니다. 그렇지 않다면, 수업 예제와 동일하게 MyBatis를 사용하여 프로젝트를 다시 구성해 보길 강력히 권고합니다. 이를 통해 SQL을 직접 제어하는 경험과 Mapper 인터페이스의 활용법을 확실히 익힐 수 있을 것입니다.

2.2. [반드시 수정] 서비스 계층(Service Layer)의 역할 및 구조

  • 문제점: 서비스 계층에서 인터페이스와 구현체의 분리가 이루어지지 않았고, 서비스의 역할이 단순 Repository 호출에 그치고 있습니다.
  • 관련 파일: src/main/java/com/roboai/mvc/service/AuditService.java

AuditService.java (L13-L24)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Service
public class AuditService {

    private final ApiAuditLogRepository repo;

    @Autowired
    public AuditService(ApiAuditLogRepository repo) {
        this.repo = repo;
    }

    /** 감사 로그 1건 저장 */
    public void save(ApiAuditLog log) {
        repo.save(log);
    }
}

수업 예제 비교 (MemberServiceImpl.java): 수업 예제에서는 MemberService라는 인터페이스를 먼저 정의하고, MemberServiceImpl이 이를 구현합니다. 또한 join 메서드 내부에서 아이디/이메일 중복 검사와 같은 여러 비즈니스 로직을 처리한 후 Mapper를 호출합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// example/main/java/kr/hossam/myshop/services/impl/MemberServiceImpl.java
@Service
@RequiredArgsConstructor
public class MemberServiceImpl implements MemberService {

    private final MemberMapper memberMapper;

    @Override
    public Member join(Member input) throws Exception {
        // 가입 과정에서 아이디와 이메일 중복 검사를 수행 (비즈니스 로직)
        Member temp1 = new Member();
        temp1.setUserId(input.getUserId());
        this.isUniqueUserId(temp1);
        // ...
        // 회원 가입 수행 후 결과 체크 (비즈니스 로직)
        if (memberMapper.insert(input) == 0) {
            throw new ServiceNoResultException("저장된 Member 데이터가 없습니다.");
        }
        return memberMapper.selectItem(input);
    }
}

개선 제안:

  1. AuditService를 인터페이스로 변경하고, 실제 구현은 AuditServiceImpl 클래스에서 하도록 구조를 변경하세요. 이는 객체 지향의 다형성을 활용하고, 향후 다른 종류의 AuditService 구현체로 쉽게 교체할 수 있게 해주는 중요한 설계 패턴입니다.
  2. 현재 save 메서드는 단순히 Repository의 save를 호출만 하고 있습니다. 지금은 로직이 단순하지만, 앞으로 로그 데이터를 저장하기 전 특정 데이터를 가공하거나, 유효성을 검증하는 등의 ‘비즈니스 로직’이 추가될 공간이 바로 서비스 계층입니다. 항상 이런 확장 가능성을 염두에 두고 코드를 작성해야 합니다.

2.3. [수정 제안] 전역 예외 처리의 부재

  • 문제점: API 컨트롤러나 서비스에서 발생할 수 있는 예외에 대한 체계적인 처리가 없습니다.
  • 관련 파일: AnalyticsController.java, RequestLoggingInterceptor.java

RequestLoggingInterceptor에서는 try-catch로 예외를 처리하여 로깅 실패가 전체 요청에 영향을 주지 않도록 잘 처리했습니다. 하지만 AnalyticsController와 같은 API 컨트롤러에서는 예외 상황을 고려한 코드가 보이지 않습니다.

수업 예제 비교 (MyApiResponseAdvice.java): 수업 예제에서는 @RestControllerAdvice를 사용하여 프로젝트 전역에서 발생하는 예외를 한 곳에서 처리합니다. 이를 통해 컨트롤러 코드는 비즈니스 로직에만 집중할 수 있고, 모든 API 응답 형식을 일관되게 유지할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// example/main/java/kr/hossam/myshop/MyApiResponseAdvice.java
@Slf4j
@RestControllerAdvice
public class MyApiResponseAdvice implements ResponseBodyAdvice<Object> {

    @ExceptionHandler(Exception.class)
    public ResponseEntity<Map<String, Object>> myExceptionHandler(Exception e, WebRequest request) {
        // ... 예외 종류에 따라 다른 HTTP Status와 메시지를 담아 응답 구성
    }

    @Override
    public Object beforeBodyWrite(Object body, ...) {
        // ... 모든 정상 응답을 일관된 JSON 구조로 래핑
    }
}

개선 제안: 수업 예제의 MyApiResponseAdvice와 같이 @RestControllerAdvice를 사용하여 전역 예외 처리기를 구현하세요. IllegalArgumentException이나 향후 발생할 수 있는 커스텀 예외 등을 이곳에서 처리하면, 각 컨트롤러 메서드마다 try-catch를 반복하지 않아도 되어 코드가 훨씬 간결하고 안정적으로 변합니다.

2.4. [수정 제안] Helper 클래스의 미사용

  • 문제점: 요청 처리 과정에서 필요한 보조 기능들을 Helper 클래스로 분리하지 않고 컨트롤러나 인터셉터에 직접 구현하고 있습니다.
  • 관련 파일: RequestLoggingInterceptor.java

RequestLoggingInterceptorfirstHeader 메서드는 여러 HTTP 헤더 중 유효한 값을 찾는 유용한 기능입니다. 하지만 이 기능은 로깅 인터셉터에만 종속될 필요가 없는 범용적인 기능입니다.

수업 예제 비교 (helpers 패키지): 수업 예제에서는 RegexHelper, FileHelper, WebHelper 등 다양한 Helper 클래스를 만들어 프로젝트 전역에서 공통으로 사용할 기능들을 분리했습니다. 예를 들어 WebHelper는 IP 주소를 얻거나 현재 URL을 구성하는 등의 웹 관련 기능을 담당합니다.

개선 제안: firstHeader와 같이 다른 곳에서도 유용하게 쓰일 수 있는 기능들은 WebHelper 같은 클래스를 만들어 분리하는 것을 권장합니다. 이렇게 하면 코드의 재사용성이 높아지고, 각 클래스는 자신의 핵심 책임에만 집중할 수 있게 됩니다.

2.5. [수정 제안] .gitignore 파일의 누락

  • 문제점: 프로젝트 루트에 .gitignore 파일이 없습니다.
  • 따끔한 충고: .gitignore는 “이 파일들은 Git이 추적할 필요가 없어!”라고 알려주는 중요한 약속입니다. 이 파일이 없으면 build 폴더의 컴파일된 파일, IDE 설정 파일(.idea, .gradle), 민감한 정보가 있을 수 있는 .env 파일 등이 모두 원격 저장소에 올라갈 수 있습니다. 이는 저장소를 불필요하게 무겁게 만들고, 동료 개발자와의 협업 시 충돌을 일으키는 원인이 됩니다. 지금 바로 Spring Boot 프로젝트에 맞는 표준 .gitignore 파일을 생성하여 적용하세요.

3. 총평

요청 로깅이라는 구체적인 목표를 설정하고 이를 인터셉터와 서비스, JPA Repository를 이용해 구현해낸 점은 칭찬할 만합니다.

다만, 수업에서 강조했던 MyBatis의 활용, 서비스 계층의 역할, 전역 예외 처리와 같은 핵심적인 패턴들을 놓치고 있기 때문에 수업 내용의 정리라는 프로젝트 본연의 목적은 이루지 못하고 있다는 점에서 전반적으로 아쉬운 결과물입니다. 기술의 ‘사용법’을 아는 것을 넘어, ‘왜 그렇게 설계해야 하는지’에 대한 고민이 더 필요해 보입니다.

이번 피드백을 계기로 다시 한번 예제 코드를 꼼꼼히 분석하며 그 안에 담긴 설계 의도를 파악해 보세요. 지금의 경험이 밑거름이 되어 분명 더 크게 성장할 수 있을 겁니다. 응원하겠습니다!

This post is licensed under CC BY 4.0 by the author.