목록2025/04 (12)
개발지식 먹는 하마 님의 블로그

🚐 배달앱 아웃소싱 프로젝트🟪 요구사항 기능조건달성 여부공통테스트 커버리지30%이상❌필수인증/인가- Bcrypt 암호화- 권한 분류✔️필수가게 CRUD- 사장님은 최대 3개의 가게만 운영 가능✔️필수메뉴 CRUD- 삭제된 메뉴는 가게 메뉴 조회 시, 나타나지 않는다. 주문 내역 조회 시에는 나타난다.✔️필수주문 CRUD- 가게 영업 시간에만 주문 가능- 최소 주문 금액을 만족해야 가능✔️필수리뷰 CRUD- 배달 완료된 주문만 작성 가능✔️도전 (택)장바구니- 한 가게의 메뉴만 담을 수 있다- 24시간 뒤 만료✔️도전 (택)대시보드 ❌도전 (택)소셜 로그인 ✔️도전 (택)알림주문 상태 알림✔️ 도전 (택) 광고 우선 표시 기능 ✔️ 🟪 맡은 역할장바구니소셜 로그인주문 상태 알림 🟪 Github ..
스케줄러로 장바구니 삭제하기(Redis를 사용하지 않는) event Schedule 방법에는 스프링에 제공하는 것과 MySQL이 제공하는 것 2가지가 있다고 한다.나는 스프링에서 제공하는 방식을 사용하였다. @Component@RequiredArgsConstructorpublic class CartCleanupScheduler { private final CartRepository cartRepository; @Scheduled(fixedRate = 1000 * 60 * 30) //30분마다 @Transactional public void deleteExpiredCarts() { //만료시간이 현재보다 이전인 경우를 조회해온다. List expiredCar..
장바구니 구현에서 핵심 기능인 부분은 메뉴 추가였다.메뉴 추가 시 고려해야할 부분이 많았다.장바구니 메뉴 추가 알고리즘유저의 장바구니가 이미 존재하는가?존재하지 않는다면 새로운 장바구니를 생성한다. // userId에 해당하는 장바구니 존재 여부를 확인하고 없다면 장바구니를 생성한다. Cart cart = cartRepository.findByUserId(userId) .orElseGet(() -> { Cart newCart = Cart.createCart(user, store); return cartRepository.save(newCart); //새로 생성해 데이터 베이스에 저장한 ..

❔ 장바구니를 어떻게 구현할 것인가에 대한 고민메뉴와 주문은 N:M 관계이다.장바구니 기능을 추가하기 위해서는 중간 테이블을 두어 1:N, 1:M 관계로 만들 필요가 있었다.✅ 장바구니 중간 테이블 구현 방법 선정DB - 제일 무난하지만 DB 접근 횟수가 증가한다.세션 - 트래픽이 많아질 경우 서버의 부담이 커진다.Redis - 팀원 모두가 개발 환경을 맞춰야 한다.Docker - Redis의 대체제이지만 여전히 별도의 학습이 필요하다.위와 같은 이유로 후보들 중 1번 DB를 사용하기로 선정하였다.✅ 장바구니 조건한 가게의 메뉴만 담을 수 있으며, 가게가 변경될 시 장바구니는 초기화된다.외래키 User를 Unique로 설정해 한 가게의 메뉴만 담을 수 있도록 하고자 하였다.Cart 삭제 시, CartIt..

로그인 관련 처리와 같이 공통으로 처리해야 하는 로직들이 있다.이러한 공통 로직은 Filter, Interceptor, AOP를 활용하여 처리할 수 있다.사용 범위는 제한적이지만 필수적으로 사용되는 요소들이다.이 셋은 동작 시점, 목적, 사용 방식에서 차이가 있다. Request가 들어올 때, Filter ➡️ Interceptor ➡️ AOPResponse가 나갈 때, AOP ➡️ Interceptor ➡️ Filter FilterRequest, Response를 걸러서 전달한다.Servlet 기반, Dispatcher Servlet 이전에 동작한다.클라이언트 → 서버로의 요청 전/후에 처리여러 필터가 체인으로 연결되어 연속으로 수행될 수 있다.인코딩 처리, 로깅, 보안 검사, 인증, CORS 등을 위..
Test Code에서 라이브러리가 일부 import 되지 않는 이슈main 코드에서만 적용되도록 설정된 Gradle부분을test에서도 적용되도록 변경하였다.Jwt Filter Url 이슈

User의 정보를 담은 Token을 쿠키에 담아 사용하기회원가입 또는 로그인 성공 시, 유저의 Id, email, userRole(권한)을 담은 토큰이 문자열로 반환되도록 설계되어있었다.@Getter public class SigninResponse { private final String bearerToken; public SigninResponse(String bearerToken) { this.bearerToken = bearerToken; } }Barere Token을 문자열 형식으로 반환하고 있었기 때문에Postman에서 직접 Authorization을 입력해줘야 하는 불편함이 있었다.이를 쿠키에 Token을 담아 사용하는 방식으로 변경하였다.public ..

즉시 로딩 Eager모든 연관 데이터를 한 번에 로드한다.여러 개의 엔티티가 자주 함께 사용될 때 유용하다.쿼리 수행 횟수를 줄여 성능을 향상할 수 있다.@ManyToOne, @OneToOne지연 로딩 Lazy데이터가 실제로 필요한 시점에만 로드한다.연관 데이터가 필요하지 않은 경우에 유용하다.네트워크 트래픽을 줄이고 메모리 사용을 최적화한다.@OneToMany, @ManyToMany즉시 로딩과 지연 로딩 비교 테스트N:1 관계의 게시글과 유저가 있다고 가정하고, 아래의 테스트 코드로 테스트를 해보자.//Board 목록만 조회 List boards = em.createQuery("SELECT b FROM Board b", Board.class).getResultList();//조회 결과 출력 for (B..

🍪 Cookie클라이언트에 저장되는 작은 데이터서버는 무상태 Stateless이기 때문에 클라이언트의 상태를 보존하지 않는다.따라서, 쿠키를 사용해 사용자의 상태 또는 세션을 유지한다.🍪 Cookie를 사용하는 과정클라이언트가 로그인을 시도한다.로그인에 성공하면, 서버는 쿠키를 만들고 Set-Cookie 헤더를 통해 응답에서 쿠키를 함께 보낸다.클라이언트는 웹 브라우저 내에 있는 쿠키 저장소에 받은 쿠키를 저장한다.이후의 요청마다 클라이언트는 가지고 있는 쿠키를 요청에 함께 보낸다.🍪 Cookie의 단점쿠키는 탈취되기 쉬우며 한번 탈취된 정보는 반영구적으로 사용할 수 있다.또한, Client가 임의로 값을 변경하면 다른 유저로 인식할 수 있다.쿠키에 중요한 값을 저장하지 않는다.암호화된 Token을..

이번 팀 프로젝트에서는 시스템으로 인해 고통받았다.이런 문제가 또 발생했을 때, 빠르게 극복해낼 수 있도록 문제와 해결 방법을 정리하고자 한다.🤦♀️ IntelliJ 'Cannot resolve symbol' 에러몇 분 전까지만 해도 잘 돌아가던 프로그램이 코드를 딱 한 단어 바꾸고 재실행했을 뿐인데 오류가 발생했다.내가 겪은 오류 메시지는 캡쳐를 해놓지 않았기 때문에 다른 블로그의 오류 이미지를 첨부한다.위의 이미지와 비슷한데 내 경우에는 UserService가 import된 경로를 찾을 수 없다는 오류였다.아무리 봐도 잘못 바꾼 코드가 없고, 경로도 너무나 정확한데 오류가 해결되지 않았다.왜냐하면 그건 내 잘못이 아니었기 때문... 😭💡 해결 방법원인은 IntelliJ에서 캐시 문제로 인해 파..