중급 프로젝트을 앞두고, 코드잇 초급 프로젝트에 대한 회고를 뒤늦게나마 정리해보려 한다..
짧은 기간이었지만, 다양한 기술과 협업을 경험할 수 있었고 새로운 도전으로 가득했던 의미 있는 프로젝트였다. 👍
⚙️ Backend Stack
📦 Framework
├── Spring Boot 3.x # 메인 프레임워크
├── Spring Data JPA # ORM 및 데이터 접근
└── Gradle # 빌드 도구
🗄️ Database
├── H2 Database # 개발 환경용 (In-memory)
└── PostgreSQL # 운영 환경용 (RDBMS)
📚 Documentation
├── Swagger/OpenAPI 3.0 # API 문서 자동화
└── Notion # 프로젝트 문서 및 협업 기록
🔧 Development Tools
├── IntelliJ IDEA # IDE
├── Git & GitHub # 버전 관리
├── Discord # 팀 커뮤니케이션
└── Postman # API 테스트**
문제
배치 데이터 백업 기능이 의도치 않게 짧은 시간 간격으로 중복 실행되었다.
원인
@Scheduled 메서드가 반복 호출되며, 해당 Bean이 중복 등록되는 것을 확인하였다.
이는 Spring Boot DevTools의 자동 재시작 기능이 활성화되어 있어 ApplicationContext가 반복 생성되면서 생긴 문제였다.
해결
DevTools의 자동 재시작 기능을 비활성화 하였다.
spring:
devtools:
restart:
enabled: false
당연히 내 코드나 환경 설정에 문제가 있을 것이라 생각했었는데, 프레임워크의 기능으로 인한 문제였다니.. 프레임워크에 대한 이해도 중요하다는 것을 깨달았다.
문제
커스텀 어노테이션 기반으로 AOP를 적용하려 했으나, @target을 사용했을 때 어노테이션 인식에 실패하는 문제가 발생하였다.
원인
@target은 런타임 객체에 선언된 어노테이션을 기준으로 필터링 하는데,
Spring AOP는 프록시 기반으로 동작하기 때문에 클래스 레벨의 어노테이션을 제대로 인식하지 못하는 경우가 있다.
즉, 프록시 객체는 실제 클래스의 어노테이션 정보를 직접 가지지 않기 때문에
@target이 제대로 인식하지 못한다.
반면,@within은 타입(클래스)에 선언된 어노테이션을 기준으로 하기 때문에, 프록시 환경에서도 안정적으로 동작한다.
해결
포인트컷 표현식을 @target -> @within 으로 변경하여 클래스에 선언된 어노테이션 기준으로 필터링되도록 하였다.
@Around("@within(com.fource.hrbank.annotation.Logging)")
public Object logExecutionTime<(ProceedingJoinPoint joinPoint) throws Throwable {
...
}
문제
아래와 같은 JPQL 조건절을 사용하였더니 PostgreSQL에서 ould not determine data type 오류가 발생하였다.
SELECT COUNT(e)
FROM Employee e
WHERE (:from IS NULL OR e.hireDate >= :from)
원인
PostgreSQL은 파라미터에 명확한 타입 정보가 없으면 실행이 불가하다. (ex. ? 또는 :param)
그래서 :from IS NULL 구문에서 타입 추론이 불가해 오류가 발생하였던 것이다.
해결
QueryDSL을 도입하여 조건을 Java 코드로 조합하였다.
// where 조건 생성을 위한 빌더
BooleanBuilder where = new BooleanBuilder();
// 검색 조건 : 작업자_부분일치, 시작시간_범위조건, 상태_완전일치
if (worker != null) {
where.and(qBackupLog.worker.contains(worker));
}
if (startedAtFrom != null && startedAtTo != null) {
where.and(qBackupLog.startedAt.between(startedAtFrom, startedAtTo));
}
if (status != null) {
where.and(qBackupLog.status.eq(status));
}
DBMS마다 SQL 동작 방식에 차이가 있으므로, 조건 분기는 가급적 Java에서 처리하는 것이 더 안정적이다.
이번 프로젝트는 첫 팀 프로젝트였기에 더욱 의미가 컸던 것 같다.
단순한 기능 구현뿐만 아니라 설계부터 협업, 형상관리 등 전체 개발 프로세스를 경험해볼 수 있었고, 팀원들과 협업하며 문제를 해결해가는 경험이 힘들기보단 재밌었다. 😁
아무래도 개발 기간이 짧았다 보니 코드나 테스트 작성의 완성도 측면에서 아쉬움도 있었지만, 이는 이후 리팩토링을 통해 더 나은 결과물로 발전시켜볼 계획이다..!💪
앞으로 진행할 많은 프로젝트에서도 이번 경험을 바탕으로, 한층 더 발전된 모습으로 진행할 수 있기를! 👍