묻고 답해요
169만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
OrderServiceTest 상문주문 테스트 시 update 쿼리 문의
안녕하세요.OrderServiceTest에 상문주문 메서드 실행 할때 쿼리를 봤는데 item에 update 쿼리가 발생했더라고요. 제가 궁금한게 item, member, order.. 등등 persist 했고 중간에 flush를 할만한 jpql이라던가 모든 엔티티 pk가 identity로 되어 있지 않은데 item에 update가 왜 발생한건가요? 엔티티를 persist 하고 flush 전에 변경 감지 set으로 수정하면 insert -> update가 나가는건가요? insert만 나가는거 아닌가요?감사합니다.
-
해결됨토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
N+1 관련해서 질문있습니다.
안녕하세요. 우선 좋은 강의 제작해주신 토비님께 항상 감사하고 있어요. 이제 배운지 1년된 왕초보입니당..혼자 배워보면서 개인 프로젝트를 만들고 있는데 JPA를 사용하고 있어요.제가 궁금한 것이... N+1 관련한 문제입니다. 아 일단 프로젝트 주제는 복식부기 가계부에요. @Entity ... public class Journal extends BaseEntity { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "ledger_id", nullable = false, updatable = false) @OnDelete(action = OnDeleteAction.CASCADE) private Ledger ledger; ... @OneToMany(mappedBy = "journal", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true) private List<EntryLine> entries = new ArrayList<>(2); ... public EntryLine getEntryLine(EntrySide side) { switch (side) { case CREDIT : this.entries.stream().filter(line -> line.isCredit()).findFirst() .orElseThrow(...); case DEBIT : this.entries.stream().filter(line -> line.isDebit()).findFirst() .orElseThrow(...); default : throw new ... } } ... // Service에서 저장되기 전에 호출 public void validateSavable() { ... validateJournalSave(); } private void validateJournalSave() { AccountType debit = getEntryLine(EntrySide.DEBIT).getAccountType(); AccountType credit = getEntryLine(EntrySide.CREDIT).getAccountType(); if(!this.transactionType.isValidPlacement(debit, credit)) { throw new ... } } }Journal Class에서 EntryLine List에 접근하고 있어요. 그리고 EntryLine Class는 이렇게 생겼어요.@Entity ... public class EntryLine extends BaseEntity { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "journal_id", nullable = false, updatable = false) private Journal journal; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "account_id", nullable = false) private Account account; ... // private package 접근제어자 사용 // Account는 Category를 참조중이에요. AccountType getAccountType() { return this.account.getCategory().getAccountType(); } } 거래가 저장되기 전에 Journal : validateJournalSave() 에서 this.transactionType에 따라 차변과 대변에 올바르게 위치하고 있는지 검사한 후 저장하고 있는데 이것을 생성과 수정할 때 두 곳에서 사용하고 있어요. Ledger에 5개 Category가 있고, Account는 그 Category를 참조하고 Category에서만 AccountType이 있어요.Journal이 각 EntryLine의 AccountType을 얻기 위해Journal -> EntryLine -> Account -> Category -> getAccountType() 이렇게 흘러가네요.이렇게 접근해도 설계상 괜찮은걸까요? Journal을 저장할때는 @Query 사용해서 Fetch Join으로 필요한 Account를 가져오고 있는 상황이에요.Journal이라는 엔티티가 비즈니스 로직 수행을 위해서 다른 엔티티의 필드까지 깊게 참조?? 가져오도록 설계하는게 옳은건지 모르겠어요.
-
미해결죽음의 Spring Batch: 새벽 3시의 처절한 공포는 이제 끝이다.
강의 중복 확인 요청
섹션 9부터 중복으로 내용이 있는데 확인 바란다
-
미해결죽음의 Spring Batch: 새벽 3시의 처절한 공포는 이제 끝이다.
중복내용 제보?!
안녕 킬구형 강의는 구매한지 오래됐지만 지금에서야 공부하고 있어 별건 아닌데! 청크처리와 트랜잭션 부분이 2번 들어가있어서 알려줄려고! 킬구형 덕분에 싸게 구매해서 정말 공부 열심히 하고 있어!다음것도 기대할게!
-
해결됨제미니의 개발실무 - 커머스 백엔드 기본편
궁금한점이 여러개 생겼습니다.
안녕하세요, 2회독 하는중입니다. 확실히 강의 활용하면서 진행하니깐 느끼기(?)가 뭔지 알것같고 궁금한점이 생기네요. CouponService 에서 이미 다운받은 쿠폰은 내려주지 않는 로직을 만들면서 궁금한점이 여러개 생겼습니다.작성하면서 정리하니깐 궁금한점이 계속 꼬리물어서 생긴다고 해야하나 그래서 의식의 흐름대로 적는 것 같아서 미리 죄송합니다 🥲/// CouponService public List<Coupon> getCouponsForMenus(Principal principal, Collection<Long> couponIds) { Set<Long> applicableCouponIds = couponFinder.findApplicableCouponIds(couponIds); Set<Long> downloadedCouponIds = issuedCouponFinder.findDownloadedCouponIds(principal.getId()); Set<Long> downloadableCouponIds = applicableCouponIds.stream() .filter(id -> !downloadedCouponIds.contains(id)) .collect(Collectors.toSet()); return couponFinder.findAllById(downloadableCouponIds); } // CouponFinder public Set<Long> findApplicableCouponIds(Collection<Long> menuIds) { List<CouponTargetEntity> menuTargets = couponTargetRepository.findByTargetTypeAndTargetIdIn( CouponTargetType.MENU, menuIds ).stream().filter(CouponTargetEntity::isActive).toList(); List<CouponTargetEntity> categoryTargets = couponTargetRepository.findByTargetTypeAndTargetIdIn( CouponTargetType.MENU_CATEGORY, menuCategoryRepository.findByCategoryIdIn(menuIds).stream() .filter(MenuCategoryEntity::isActive) .map(MenuCategoryEntity::getCategoryId).collect(Collectors.toSet()) ); return Stream.concat(menuTargets.stream(), categoryTargets.stream()) .map(CouponTargetEntity::getCouponId) .collect(Collectors.toSet()); } CouponService 에서 implement 계층 CouponFinder, IssuedCouponFinder 를 의존하고 있는 상황에서 각 계층끼리 소통할 때강사님 Q/A 답변중에서 가능한 db 엔티티 대신에 개념객체를 넘기는 방향으로 설계하는걸 지향하신다고 말씀하신 답변이 기억이 나는데요, 지금 상황에서는 개념객체까지 변환하는것보다는 id 컬렉션만 뽑아내서 넘기는게 더 효율적이고 깔끔한 것 같습니다.이런 패턴(?) 이 자주 나올 것 같아서 괜찮은지 궁금 합니다.꼭 id 컬렉션을 넘기는게 아니더라도 entity를 반환해야만 매핑하기 편한 경우가 많을 것 같더라구요회원/비회원 요구사항https://inf.run/GTxAx해당 Q/A 를 보기도 했고 마침 강의 활용해서 커피 판매 어플리케이션을 만들고 싶어서 비회원처리가 필요했습니다.public class PrincipalArgumentResolver implements HandlerMethodArgumentResolver { private static final String PRINCIPAL_ID_HEADER = "Gu-Coffee-Principal-Id"; private static final String PRINCIPAL_TYPE_HEADER = "Gu-Coffee-Principal-Type"; @Override public boolean supportsParameter(MethodParameter parameter) { return parameter.getParameterType().isAssignableFrom(Principal.class); } @Override public @Nullable Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); if (request == null) throw new CoreException(ErrorType.INVALID_REQUEST, null); Authenticated annotation = parameter.getParameterAnnotation(Authenticated.class); String id = request.getHeader(PRINCIPAL_ID_HEADER); String type = request.getHeader(PRINCIPAL_TYPE_HEADER); validatePrincipal(annotation, id, type); return Principal.of(id, type); } private void validatePrincipal(Authenticated annotation, String id, String type) { boolean isRequired = (annotation != null && annotation.required()); if (isRequired) { if (id == null) throw new CoreException(ErrorType.UNAUTHORIZED, null); if (!PrincipalType.USER.name().equals(type)) { throw new CoreException(ErrorType.UNAUTHORIZED, "회원 전용 서비스입니다."); } } if (type == null) { throw new CoreException(ErrorType.INVALID_REQUEST, null); } } } @Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) public @interface Authenticated { boolean required() default true; } 저는 Authenticated 라는 메타 어노테이션을 사용해서@GetMapping("/v1/issued-coupons") private ApiResponse<List<IssuedCouponResponse>> getIssuedCoupons(@Authenticated Principal principal) { List<IssuedCoupon> issuedCoupons = issuedCouponService.getIssuedCoupons(principal); return ApiResponse.success(IssuedCouponResponse.from(issuedCoupons)); } @PostMapping("/v1/coupons/{couponId}/download") private ApiResponse<?> download( Principal principal, @PathVariable Long couponId) { couponService.download(principal, couponId); return ApiResponse.success(); } public void download(Principal principal, Long couponId) { if (principal.getType().equals(PrincipalType.GUEST)) { throw new CoreException(ErrorType.UNAUTHORIZED, "비회원은 쿠폰을 다운로드할 수 없습니다."); } 애초에 회원만 접근 가능한 api 는 어노테이션으로 처리하던가 서비스 단에서 예외를 던지게 할 수도 있을 것 같아서 이렇게 만들어 봤습니다.이런식의 접근이 괜찮은지당장은 인증객체(Principal)? 랑 User 랑은 다른 개념일 것 같아서 강의내 User 대신에 사용했습니다.@Table(name = "cart_item") @Entity public class CartItemEntity extends BaseEntity { private Long userId; private String guestKey; @Enumerated(EnumType.STRING) private PrincipalType principalType; private Long menuId; private Long quantity; } public class Cart { private Principal principal; private List<CartItem> items; } 마지막으로 장바구니에 비회원도 담을 수 있다는 요구사항이 있다고 가정하면 테이블에는 userId 만 둬서 null 로 구분하는 것 보다 key 필드를 추가하고public interface CartItemRepository extends JpaRepository<CartItemEntity, Long> { @Query("SELECT c FROM CartItemEntity c " + "WHERE (:type = 'USER' AND c.userId = :id) " + "OR (:type = 'GUEST' AND c.guestKey = :key)") List<CartItemEntity> findByPrincipal( @Param("type") String type, @Param("id") Long id, @Param("key") String key ); } 이런식으로 쿼리로 분기처리해야지 생각했는데요. 예전 강사님 Q/A 답변 중에서 쿼리중심의 어플리케이션이 되는걸 지양한다는 느낌의 답변을 봤던 것 같습니다.AI 한테 물어봤을때는 해당 로직은 필터링 목적이니 쿼리로 처리하는게 성능상 효과적이라는 답변을 줬는데요.List<IssuedCouponEntity> issuedCoupons = issuedCouponRepository.findByUserId(userId) .stream() .filter(IssuedCouponEntity::isActive) .toList(); isActive 인 데이터를 가져올 때 강사님이 findByXXAndStatus 로 한번에 가져오거나 혹은 위처럼 filter로 처리하는 두가지 패턴을 보여주셨습니다.이걸 처음 봤을 때, 미세한 성능보다는 filter 로 직관적으로 활성상태인 데이터만 가져온다는 목적을 코드로 보여주는게 더 괜찮겠다고 생각했는데요.근데 이것도 결국 필터링 목적이니깐 메서드 네이밍(findByXXAndStatus)으로 처리하는게 일관성 있는게 아닌지 궁금합니다.
-
미해결핵심만 빠르게 끝내는 실전 카프카(kafka)
혹시 수업자료는 다운받을 수 있는 곳이 없나요?
강의에서 설명해주시는 내용이 깔끔히 정리되어 있는거 같아, 자료로서 볼 수 있는 링크나 파일이 있을까요? github 소스 외에 설명해주시는 강의 자료도 있으면 이론 내용 복습 차원에서 좋을 것 같아 문의 드립니다.
-
해결됨카카오 면접관의 실무 밀착형 Spring Batch: 대용량 데이터 처리의 모든 것
여러 파드 환경에서 단일 실행 보장 방식
단일 실행이 보장되는 이유로 Db를 통해서 값을 가져오고 있으며 db에서 동시성을 방어해주고 있기 때문이라고 해주셨는데요.FindRunningJobExecutions 는 단순 select문이 아니고 내부적으로 비관적 락으로 동시 접근을 막아주는 구조인가요? 저는 여러 파드인 상황에서는 보여주신 코드가 동시성 이슈로 인해 주어진 잡이 한 번만 실행된다는 것을 보장하기 힘들 것 같다고 생각했습니다.이외에도 여러 파드인 상황이라면 실무에서 어떤 요소를 고려하는지 궁금합니다.학습 내용에선 currentimestamp를 잡 파라미터에 넣어서 매번 새로운 잡 인스턴스로 취급/실행하는 형태를 보여주셨는데, 이로인해 멀티파드 환경에서 특정 잡의 중복 실행 방지 혹은 특정 잡 파라미터 구성에서의 중복 실행 방지에 대한 요건 구현 시 영향도/고려 사항이 있는지 여부와 아니면 currentimestamp 잡 파라미터를 실무에서 빼기도 하는지 궁금합니다운영 시 중복 실행 문제 및 잡 재시도에 대해 고민하다 나온 질문입니다. 혹시 애초에 대부분의 배치 잡과 스탭 로직을 멱등하게 동작하도록 설계 및 코드 작성을 해야하는 걸까요?
-
해결됨The 10x AI-Native Developer: 회사에서 AI로 압도적 성과를 내는 법
훅 내용 안뜸 오류
hook이 적용돼야하는데, 5:57의 훅이 실행된게 터미널에 강사님처럼 뜨지는 않습니다. 다른 세팅을 해줬기 때문일까요?
-
미해결최신 Spring AI 기초부터 MCP까지
챗봇 기능 구체화 (RAG)
강사님 그 gemini api 를 사용해서 챗봇 응답을 만들고 있습니다.RAG 를 이용해서 만개의 레시피 사이트에 내용을 json 파일로 가져온 다음 Vector _store 에 저장하고이거를 기반으로 사용자가 질문을 하면 그거에 맞는 레시피를 보여주고 미리 저장되어 있는 OCR을 통해 저장한 식재료 중에 부족한 식재료는 어떤 게 있고 이런걸 알려주는 기능을 만들었는데 (앱으로 카드 UI ) 형태로 보여줄려고 만들었습니다. 근데 이 기능이 무료 api 에서 는 한계가 있는건가요?
-
미해결자바와 스프링 부트로 생애 최초 서버 만들기, 누구나 쉽게 개발부터 배포까지! [서버 개발 올인원 패키지]
패키지 구분에 대해 궁금한게 있습니다
요즘은 domain별로 패키지를 나눈다고 들었는데 강의에서는 역할별로 패키지를 나누고 있어서요.어떻게 나누는게 좋은건가요?!\
-
미해결Spring Boot와 React로 배우는 초간단 REST API 게시판 만들기
스프링부트 서버 에러나요
Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.2026-04-28T17:27:55.938+09:00 ERROR 27483 --- [prac] [ main] o.s.b.d.LoggingFailureAnalysisReporter :
-
해결됨The 10x AI-Native Developer: 회사에서 AI로 압도적 성과를 내는 법
2026.04에 추가된 강의 시청 불가
2026.04에 업데이트된 일부 강의들을 시청할 수 없습니다. 유튜브 영상은 재생되는데, 나머지는 보이지 않습니다. 어떻게 해결하나요? 안보이는 강의명 예시를 하나 말씀드리면 '11. 1-9. [2026.04] 🚀 2026 Claude Code 2.0 주요 업데이트 기능 안내'입니다.
-
미해결스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 캐시 전략
Service Create/Update Record 운용과 Delete Record 미운용의 차이 질문
안녕하세요, 선생님! 강의 들으면서 Service 및 Cache Handler 구성간 Create/Update <-> Delete 간의 Return Record 여부 차이에 대해 질문드리고자 합니다. 서비스도 그렇고, 핸들러도 그렇고, Return type이 Create/Update는 전달받은 Record를 Entity Context에 적용한 객체인데, delete의 경우 void로 운용하시는 것을 보고 어떤 차이점이 있는지 궁금하여 질문드리게 되었습니다. create, update 모두 반영 이후의 내역을 바로 반환하여 보여준다는 의미로 생각하였는데, delete를 보면 그런 것 같지는 않아서 따로 다른 의미가 있는지(실무에서 이렇게 많이 사용한다던가), 별 의미가 없는지 구체적으로 문의드리고자 합니다. 감사합니다.
-
미해결카카오,구글 SNS 로그인(springboot3, vue3)
인가 코드 발급(프론트 vs 백)
카카오 데브톡 응답에 따르면 프론트/백엔드 분할 책임 방식이 지양한다고 합니다.오히려 백에서 로그인 구현을 일임하는 것을 권장합니다.https://devtalk.kakao.com/t/oauth/136448 해당 블로그에서도 책임을 프론트와 백엔드가 나누어 가지는 방식이 잘못되었다고 합니다.https://cafe.naver.com/xxxjjhhh/296 카카오 로그인 REST API에서도 백에서 인가 코드를 받습니다.https://developers.kakao.com/docs/ko/kakaologin/rest-api#before-you-begin-process 어떤 방식이 맞는지 헷갈립니다.
-
해결됨알면 칼퇴하는 Spring Boot 백엔드 바이브 코딩 맛보기
인텔리제이 MCP 서버 설정 관련
저는 인텔리제이에서 Claude App 이 안뜨고 Codex만 뜨네요... Claude App 자동 구성은 왜 안뜰까요....
-
해결됨제미니의 개발실무 - 커머스 백엔드 레거시와 AI 활용편
Sequence 관련 질문
저는 review image reordering을 염두에 두고 sequence를 계속 가져가는 방향으로 결정했습니다.제미니님은 실무에서 이렇게 염두에 두고 구현이 조금 더 복잡해지더라도 이런 특정 필드나 기능을 가져가는것을 오버엔지니어링이라고 생각하여 피하시는 편인가요? 어떤 기준으로 결정하시는지 궁금합니다.
-
해결됨제미니의 개발실무 - 커머스 백엔드 레거시와 AI 활용편
Image Only Query
image only query를 설명해주실때 stable pagination 때문에 조인을 사용하신다고 이해했습니다. 저는 다른방식으로 구현해보았는데 제미니님의 의견이 궁금합니다. 코드를 보면 나중에 image를 fetch 또 하게 되는데 이렇게되면 image only query를 ReviewImageEntity에 reviewId, status 인덱스를 걸고SELECT review FROM ReviewEntity review WHERE review.targetType = :targetType AND review.targetId = :targetId AND review.status = :status AND EXISTS ( SELECT image.id FROM ReviewImageEntity image WHERE image.reviewId = review.id AND image.status = :status 이렇게 구현하는 방식이 데이터량이 많아진다면 효율적이지 않을까 싶어서 구현해봤습니다.
-
해결됨The 10x AI-Native Developer: 회사에서 AI로 압도적 성과를 내는 법
프롬프트, 스킬, sub-agent
좋은 강의 잘 듣고 있습니다. 프롬프트, 스킬, sub-agent와 같이 여러가지 방식으로 claude code를 잘 활용하는 방법들이 있는데요,이것을 사용하는 기준들이 있는지 궁금합니다. 즉, 어떨때는 프롬프트를 쓰고, 어떨때는 sub-agent보다는 스킬을 만들다와 같은 어떤 기준들이 있을까요?
-
미해결토비의 클린 스프링 - 도메인 모델 패턴과 헥사고날 아키텍처 Part 1
도메인 모델에서 관계와 규칙을 구분하는 방법
안녕하세요. 도메인 모델을 설명하는 부분에서 관계와 규칙을 온라인 서점 운영 예시로 간단히 언급해주셨습니다.강의를 들으면서 실제 업무 도메인에 관계와 규칙을 구분지으며 간단히 개인 실습을 해보았는데요.적다보니 어느샌가 관계에 규칙이 섞이기도 하더라고요. 문득 이런 생각이 들었습니다. '관계와 규칙의 차이는 무엇인가?'관계와 규칙을 명확하게 구분지으려면 어떤 기준을 갖고 생각해야할까요? 관계는, DB 모델에 워낙 익숙하다보니 하나의 OO은 여러 OOO을 갖는다. 이쪽으로 먼저 생각이 흐르기도 하고요. 예시로 들어주신 것을 보면 관계는, '비즈니스에서 관계'라는 생각이 듭니다. 규칙은 데이터를 변경할 때 필요한 조건이라고 생각하면 될까요?토비님 의견이 궁금합니다.
-
미해결RabbitMQ를 이용한 비동기 아키텍처 한방에 해결하기
16 강의 메세지 retryCount의 의도
retryCount가 전역에서 작동하는것이 좀 수상하네요. 각 메세지의 retryCount를 세어야 할 것 같은데 저렇게 두면 혹시 메세지들이 들어오면 숫자가 마구 이랬다 저랬다 할 가능성은 없나요? 이를테면 메세지 두세개가 들어왔는데 첫번째는 실패 두번째 메세지 실패 세번째 성공 이런 경우 첫번째 메세지가 성공할때까지 sync하게 작동하여 완전 처리될때까지 두번째 메세지는 아예 실행되지 않는것인가요? 아니면 평행하게 동시에 실행되는거라 저 코드가 좀 잘못된 건가요?