분류 전체보기 11

[회고] 스케줄러 하나를 붙이려다 정책과 리뷰 단위를 다시 생각하게 된 이유

Onion Store와 내가 맡은 범위프로젝트 명: onion store인원: 5명프로젝트 기간: 10일배경: 한 명의 판매자가 직접 상품을 판매하는 단일 판매자 쇼핑몰주요 기능: 고객의 장바구니·주문·결제, 관리자의 상품·주문 관리, 채팅(cs)담당 : 결제, 환불요약처음에는 결제 기능을 구현하고, 결제창을 닫은 사용자의 미완료 주문을 정리하는 스케줄러를 붙이면 된다고 생각했다. 하지만 결제창 이탈을 처리하려는 작은 요구사항은 결제 상태 확인, 웹훅, 금액 불일치 취소, 부분 환불, 외부 결제 취소 결과를 다시 확인하는 방법에 대한 고민까지 이어졌다. 이번 글에는 짧은 기간의 팀 프로젝트에서 정책이 정해지지 않은 기능을 구현으로 먼저 해결하려 했을 때, 설계와 PR 단위가 왜 함께 어려워졌는지 돌아본다..

팀 프로젝트 2026.09.20

[Spring] 기존 Todo API 리팩터링 - Boot 4 마이그레이션부터 QueryDSL 검색까지

요약기존 Todo API를 Spring Boot 4.1.1 환경으로 옮기면서 단순히 의존성 버전만 올리는 데서 끝내지 않았다. JWT 인증 정보의 전달 방식, 매니저 등록 실패 시 남겨야 하는 로그의 트랜잭션 경계, 조회 API의 N+1과 집계 검색까지 함께 정리했다.이 글에서 다루는 범위는 다음과 같다.Java 17·Spring Boot 4.1.1·Gradle Wrapper 8.14.3 기반 실행 환경 정비HttpServletRequest attribute 기반 JWT 전달 구조를 Spring Security 표준 구조로 전환관리자 역할 변경 접근 로그와 담당자 등록 요청 로그의 목적 분리Todo 생성과 담당자 자동 등록, 요청 로그의 독립 트랜잭션 처리JPQL fetch join·@EntityGrap..

Spring 2026.09.04

[Spring Security] JWT 인증 성공 로그가 있는데 관리자 API가 401이었던 이유

배경Spring Security로 JWT 인증·인가 구조를 전환한 뒤, USER 권한 토큰으로 관리자 API를 호출했다.기대한 결과는 403 Forbidden이었다.인증 성공하지만 ADMIN 권한 없음→ 403 Forbidden그런데 실제 응답은 401 Unauthorized였다. 더 혼란스러웠던 점은 애플리케이션 로그에 JWT 인증 성공까지 남았다는 것이다.JwtFilter 진입 - uri=/admin/users/1JWT 인증 성공 - userId=1, role=USER이 글은 “토큰 검증은 성공했는데 왜 최종 응답은 401이었는가”를 추적하며, JWT 필터와 Spring Security 인가 정책의 책임을 다시 나눈 기록이다.먼저 구분할 것: 401과 403은 실패 지점이 다르다관리자 API의 응답은..

Spring 2026.09.04

[AWS] GitHub Actions OIDC부터 ALB까지, Spring Boot 배포 경로 만들기

팀 프로젝트의 Spring Boot 애플리케이션을 AWS에 배포했다. 목표는 단순히 EC2에서 애플리케이션을 실행하는 것이 아니었다. main 브랜치에 반영된 코드가 테스트를 거쳐 Docker 이미지로 만들어지고, HTTPS 도메인으로 접근 가능한 상태까지 이어지는 배포 경로를 만드는 것이 목표였다.이번 환경은 기능 검증 후 비용 관리를 위해 삭제한 임시 배포 환경이다. 따라서 아래 내용은 운영 환경을 완성했다는 기록이 아니라, 배포에 필요한 구성 요소가 어떻게 연결되는지 직접 확인한 기록이다.코드에 꺾쇠로 표시한 계정 ID, 저장소, 프로젝트명, 커밋 SHA 같은 식별자는 실제 구조를 유지하면서 일반화한 예시다.배포 경로전체 흐름은 아래와 같다.GitHub Actions (main push) ├─ 테..

AWS 2026.09.02

[Spring] GlobalExceptionHandler와 AOP 로깅 - 응답과 실패 기록의 책임 나누기

팀 프로젝트에서 AOP와 로깅 작업을 맡았다. 처음에는 Controller의 반복 로그를 AOP로 묶고, 예외는BusinessException으로 던져 GlobalExceptionHandler에서 공통 응답으로 바꾸면 충분하다고 생각했다.하지만 결제·환불 흐름에 업무 로그를 추가하는 중, 두가지 의문이 생겼다.이 실패를 클라이언트에 어떤 HTTP 응답으로 돌려줄 것인가?이 실패를 개발자가 나중에 어떤 정보로 추적할 것인가?둘은 관련 있지만 같은 책임은 아니다. 이 글에서는 커머스 결제 시스템에 적용한GlobalExceptionHandler, Controller AOP, 결제·환불 업무 로그를 기준으로 응답과 기록의 책임을나눈 과정을 정리한다.GlobalExceptionHandler는 오류 응답을 통일한다..

Spring 2026.08.25

[Docker Compose]Docker를 도입했는데, 왜 개발 환경은 똑같아지지 않을까?

문제mbti 프로젝트에서 Docker를 도입하고 CI/CD부터 AWS 인프라까지 직접 구성을 했다.과제 완료 후에 CI/CD 흐름을 이해하기 위해 그리면서 의문이 생긴것이다."Docker를 사용하면 개발자마다 동일한 환경을 만들 수 있다고 하지 않았나?" 그런데 막상 내가 구성한 환경을 돌아보니 조금 달랐다.나는 Docker Image를 만들고, 이를 Docker Hub에 Push한 뒤 EC2에서 Pull해서 컨테이너를 실행하고 있었다.Docker Image ↓ Docker Hub ↓ EC2 ↓Docker Container애플리케이션을 배포하는 데에는 문제가 없었다.하지만 여기서 이상한 점을 발견했다.이것만으로는 여러 개발자가 동일한 개발 환경을 제공해준다고,,?내가 처음 생..

Docker 2026.08.11

[AWS S3] IAM Role로 만든 Presigned URL이 7일을 못 버티는 이유

문제: 코드에 적은 기간과 실제 유효기간이 다르다프로젝트에서 회원 프로필 이미지를 S3에 저장하고, 다운로드는 Presigned URL로 처리하도록 구현했다.과제 요구사항은 "Presigned URL의 유효기간을 7일로 설정"하는 것이었고 코드에도 그대로 반영했다.private static final Duration PRESIGNED_URL_EXPIRATION = Duration.ofDays(7);public String createPresignedUrl(String key) { GetObjectRequest getObjectRequest = GetObjectRequest.builder() .bucket(bucket) .key(key) .b..

AWS 2026.08.06

GitHub Actions와 AWS OIDC 기반 CI/CD 구축 및 인증 오류 해결

배경Spring Boot 프로젝트에 GitHub Actions로 CI 파이프라인을 구축하고, AWS OIDC 인증으로 ECR에 Docker 이미지를 Push하는 자동화를 실습했다.Workflow 파일을 작성하는 것 자체보다, 실제로 파이프라인을 돌리면서 마주친 인증·권한 문제를 해결하는 과정이 이번 실습의 핵심이었다.구축한 흐름은 다음과 같다.Git Push ↓GitHub Actions 실행 ↓Gradle Test ↓Spring Boot JAR Build ↓AWS OIDC 인증 ↓ECR Login ↓Docker Image Build & Push1. GitHub Actions Workflow 파일 Push 실패.github/workflows/ci.yml을 작성하고 Push하는데 다음 오류가 발생..

TIL 2026.08.01

[Spring] 엔티티와 DTO는 왜 타입이 달라도 되는가

배경"엔티티 필드와 DTO 필드는 이름과 타입이 같아야 한다"고 배웠던 것 같은데, 실제 실습 코드를 보면 그렇지 않았다.// 엔티티@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;@ManyToOne(fetch = FetchType.LAZY)private User user;// 응답 DTOpublic class PostDto { private long Id; private String username; // user 객체가 아니라 username 문자열}이름도 다르고(user → username), 타입도 다르다(Long → long, 객체 참조 → 문자열). 그런데도 이 코드는 실제로 잘 동작한다. 왜 그런지, 그리..

Spring 2026.07.28

[Spring Boot] 프로젝트 실행 실패 원인 분석 - JWT 시크릿 키 누락과 MySQL 포트 충돌

배경레벨 0 요구사항은 "프로젝트를 실행했으나 특정 에러로 인해 실행에 실패했다, 원인을 분석해서 실행 가능하게 만들어라(에러는 여러 개일 수 있다)"였다. 실제로 처음 받은 프로젝트를 그대로 실행하면 최소 두 가지 지점에서 막혔다.문제 1. JWT 시크릿 키 미설정JwtUtil에서 시크릿 키를 다음과 같이 외부 설정값으로 받고 있었다.@Value("${jwt.secret.key}")private String secretKey;프로젝트에는 application.yml 자체가 없어서 jwt.secret.key 값을 어디서도 읽을 수 없었고, 애플리케이션이 뜨는 시점에 실패했다.문제 2. MySQL 포트 충돌application.yml을 작성하면서 MySQL 연결을 시도했는데, 3306 포트를 다른 프로그램..

TIL 2026.07.27