묻고 답해요
158만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결김영한의 실전 데이터베이스 - 기본편
SELECT 절에서 CASE문을 사용한 이후, ORDER BY 절에서도 동일한 CASE 문을 사용하는 경우
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)[질문 대상 위치]강의 영상:03:00 지점강의록:6. CASE 문.pdf - CASE 문 기본2 - CASE 문과 사용 위치[질문 내용]안녕하세요, 영한님.다음과 같이 'SELECT 절에서 사용한 CASE 문을 그대로 복사-붙여넣기 해서 ORDER BY 절에서 사용(THEN 결과는 1, 2, 3으로 변경)' 하셨는데요(아래 코드. 방법1).ORDER BY CASE WHEN price >= 100000 THEN 1 WHEN price >= 30000 THEN 2 ELSE 3 END ASC 실습하던 중 아래 코드(방법2)와 같이 'SELECT 절에서 정의한 컬럼 별칭(price_label)과 THEN 값('고가', '중가', 저가')을 이용'해서 정렬할 수도 있겠다고 생각해서 시도해봤고, 실행이 성공하여 영한님 코드와 동일한 결과가 나왔습니다.ORDER BY CASE WHEN price_label = '고가' THEN 1 WHEN price_label = '중가' THEN 2 WHEN price_label = '저가' THEN 3 END ASC 이때 자바와 같은 프로그래밍 언어 입장에서 보면 방법2의 경우가 'SELECT 절의 CASE 문 로직이 바뀌더라도 ORDER BY 절의 CASE 문 로직은 변경할 필요가 없으므로' 유지보수성 측면에서 더 좋겠다고 판단했습니다. 그런데 다음과 같은 반론(?)도 생각나더라구요.(애플리케이션 로직과 달리) 쿼리 문은 엄청나게 길고 복잡해질 가능성이 적으므로, 그냥 방법1처럼 복사-붙여넣기 해서 써도 무방하다.오히려 ORDER BY 절의 CASE 문을 새롭게 작성하는 과정에서 오타가 발생하여 오류를 일으킬 위험이 존재하니, 그냥 방법1처럼 복사-붙여넣기 해서 써도 무방하다.간단한 쿼리의 경우 ORDER BY 절의 CASE 문을 새롭게 작성하는 데에 비교적 많은 시간이 소모되는 비효율성이 존재하니, 그냥 방법1처럼 복사-붙여넣기 해서 써도 무방하다.이에 대해서 영한님의 의견은 어디에 가까우신가 의견 여쭙고 싶어서 글 남깁니다!
-
해결됨스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판
MSA 아키텍쳐 DB관게
MSA 구조에서 DB는 FK로 묶여있지 않고 다 분리된 테이블일까요?아니라면 ERD 다이어그램도 추가해 주실 수 있으실까요?
-
해결됨스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판
MSA 아키텍쳐 DB관게
MSA 구조에서 DB는 FK로 묶여있지 않고 다 분리된 테이블일까요?아니라면 ERD 다이어그램도 추가해 주실 수 있으실까요?
-
해결됨김영한의 실전 데이터베이스 - 기본편
[수정 건의] 문제3의 실행 결과
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)[질문 내용]안녕하세요, 영한님. 중요한 내용은 아니지만, 강의자료 개선에 조금이나마 도움이 될까 해서 글 남깁니다.요약: 문제3의 [실행 결과]의 행 순서를 변경하는 것을 제안합니다. 본론:문제3: 회사 주요 이벤트 타임라인 만들기의 경우 '최신 이벤트가 가장 위에 오도록 내림차순으로 정렬'할 것을 요구하고 있습니다. 현재 강의록의 [실행 결과]에는 다음 사진과 같이 고객 가입 이벤트가 모두 상품 주문 이벤트 보다 아래에 위치하고 있고, "고객 가입 이벤트의 날짜는 쿼리 실행 시점의 created_at 값에 따라 달라질 수 있다" 고 명시되어 있습니다. 수강생들이 위 사진만 얼핏 보고 섣불리 접근한다면, "(고객 생성 일시)가 쿼리 실행 시점에 따라 달라지기는 하지만, 그래도 모든 상품 주문 이벤트 이후에 위치하는게 올바른 결과겠구나"라고 판단할 위험이 있다고 생각해서 다음과 같이 수정하면 어떨까 의견을 제시합니다.수정 이후: 모든 고객 가입 이벤트 행이 모든 상품 주문 행보다 위에 위치. 즉, 현재 [실행 결과]에서 두 이벤트의 위치를 맞바꿈. 수정 제안에 대한 근거는 다음과 같습니다.예제 데이터를 삽입할 때 users.created_at 컬럼의 경우 DEFAULT CURRENT_TIMESTAMP로 추가했음.수강생은 강의 오픈 날짜인 2025-08-01 이후에 쿼리를 실행하는 것이 보장되기 때문에 users.crated_at 컬럼의 값 또한 마찬가지로 보장됨.상품_주문 이벤트의 이벤트_날짜 중 가장 최근은 2025-06-17로 강의 오픈 날짜보다 이전임. 따라서 모든 상품_주문 이벤트 또한 마찬가지로 보장됨.따라서 모든 users.created_at 데이터는 orders.order_date보다 최근임이 보장됨. 아래 사진은 참고용으로, 수강생인 제 경우의 실행 결과입니다. 위에서 설명한 바와 같이 고객 가입 이벤트가 항상 상품 주문 이벤트보다 그 시점이 최근임을 확인할 수 있습니다.
-
해결됨스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판
CommentApiTest 중 오류가 발생합니다
섹션3 23강 댓글 최대 2 depth - CUD API 테스트 & 테스트 데이터 삽입 파트 수강 중create 테스트를 실행하는데 오류가 계속 발생합니다package kuke.board.comment.api; import kuke.board.comment.service.response.CommentResponse; import lombok.AllArgsConstructor; import lombok.Getter; import org.junit.jupiter.api.Test; import org.springframework.web.client.RestClient; public class CommentApiTest { RestClient restClient = RestClient.create("http://localhost:9001"); @Test void create() { CommentResponse response1 = createComment(new CommentCreateRequest(1L, "my comment1", null, 1L)); CommentResponse response2 = createComment(new CommentCreateRequest(1L, "my comment2", response1.getCommentId(), 1L)); CommentResponse response3 = createComment(new CommentCreateRequest(1L, "my comment3", response1.getCommentId(), 1L)); System.out.println("commentId=%s".formatted(response1.getCommentId())); System.out.println("\tcommentId=%s".formatted(response2.getCommentId())); System.out.println("\tcommentId=%s".formatted(response3.getCommentId())); // commentId=123694721668214784 // commentId=123694721986981888 // commentId=123694722045702144 } CommentResponse createComment(CommentCreateRequest request) { return restClient.post() .uri("/v1/comments") .body(request) .retrieve() .body(CommentResponse.class); } @Getter @AllArgsConstructor public static class CommentCreateRequest { private Long articleId; private String content; private Long parentCommentId; private Long writerId; } } 코드는 강사님 코드를 복붙을 해 봤는데도 아래의 오류가 지속적으로 발생합니다500 Internal Server Error: "{"timestamp":"2025-08-24T11:27:31.828+00:00","status":500,"error":"Internal Server Error","path":"/v1/comments"}"org.springframework.web.client.HttpServerErrorException$InternalServerError: 500 Internal Server Error: "{"timestamp":"2025-08-24T11:27:31.828+00:00","status":500,"error":"Internal Server Error","path":"/v1/comments"}"at org.springframework.web.client.HttpServerErrorException.create(HttpServerErrorException.java:102)at org.springframework.web.client.StatusHandler.lambdadefaultHandlerdefaultHandler3(StatusHandler.java:89)at org.springframework.web.client.StatusHandler.handle(StatusHandler.java:146)at org.springframework.web.client.DefaultRestClient$DefaultResponseSpec.applyStatusHandlers(DefaultRestClient.java:698)at org.springframework.web.client.DefaultRestClient.readWithMessageConverters(DefaultRestClient.java:200)at org.springframework.web.client.DefaultRestClient$DefaultResponseSpec.readBody(DefaultRestClient.java:685)at org.springframework.web.client.DefaultRestClient$DefaultResponseSpec.body(DefaultRestClient.java:631)at kuke.board.comment.api.CommentApiTest.createComment(CommentApiTest.java:32)at kuke.board.comment.api.CommentApiTest.create(CommentApiTest.java:14)at java.base/java.lang.reflect.Method.invoke(Method.java:580)at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) DB를 봤을 때 parent_comment_id 가 NOT NULL이라 안 되나 싶다가도강사님은 pdf의create table comment (comment_id bigint not null primary key,content varchar(3000) not null,article_id bigint not null,parent_comment_id bigint not null,writer_id bigint not null,deleted bool not null,created_at datetime not null);sql문과 똑같이 만들었던걸 생각하면 아닌 거 같기도 하고 뭐가 문제인지 감이 안 옵니다 @Service @RequiredArgsConstructor public class CommentService { private final Snowflake snowflake = new Snowflake(); private final CommentRepository commentRepository; @Transactional public CommentResponse create(CommentCreateRequest request) { Comment parent = findParent(request); Comment comment = commentRepository.save( Comment.create( snowflake.nextId(), request.getContent(), parent == null ? null : parent.getCommentId(), request.getArticleId(), request.getWriterId() ) ); return CommentResponse.from(comment); } private Comment findParent(CommentCreateRequest request) { Long parentCommentId = request.getParentCommentId(); if ( parentCommentId == null) { return null; } return commentRepository.findById(parentCommentId) .filter(not(Comment::getDeleted)) .filter(Comment::isRoot) .orElseThrow(); } public CommentResponse read(Long commentId) { return CommentResponse.from( commentRepository.findById(commentId).orElseThrow() ); } @Transactional public void delete(Long commentId) { commentRepository.findById(commentId) .filter(not(Comment::getDeleted)) .ifPresent(comment -> { if (hasChildren(comment)) { comment.delete(); } else { delete(comment); } }); } private boolean hasChildren(Comment comment) { return commentRepository.countBy(comment.getArticleId(), comment.getCommentId(), 2L) == 2; } private void delete(Comment comment) { commentRepository.delete(comment); if (!comment.isRoot()) { commentRepository.findById(comment.getParentCommentId()) .filter(Comment::getDeleted) .filter(not(this::hasChildren)) .ifPresent(this::delete); } } }서비스의 경우에도 딱히 차이점을 발견하지 못했고 혹시나 싶어 복붙을 해도 마찬가지의 오류가 발생합니다
-
미해결자바와 스프링 부트로 생애 최초 서버 만들기, 누구나 쉽게 개발부터 배포까지! [서버 개발 올인원 패키지]
38강 editConfiguration에 active profiles 가 없어요
위와 같이 active profiles가 없는데 어떻게 하면 될까요?
-
해결됨6주 완성! 백엔드 이력서 차별화 전략 4가지 - 똑같은 이력서 속에서 돋보이는 법
로컬 모니터링 구축에 대해 질문있습니다.
Actuator, 프로메테우스, Grafana를 통해 운영 서버 모니터링 대시보드를 구축하려고 합니다. (CloudWatch는 일정 용량 이후 비용이 부과되는 문제로 Prometheus를 선택했습니다.)근데 운영 서버에서 Actuator와 프로메테우스 서버를 띄우면 외부에서도 Actuator 가 제공하는 메트릭 정보뿐만 아니라, 프로메테우스가 수집한 정보를 9090 포트 번호를 통해 접근 가능하다고 생각하였습니다.처음에는 Actuator의 정보를 ADMIN 권한을 가진 관리자만 접근 가능하도록 설정하였지만, Prometheus 서버에서 접근이 불가능한 문제가 생겼습니다.Prometheus와 Grafana를 통해 모니터링을 구축할 때, 데이터에 대한 보안을 강화할 수 있는 방법이 뭐가 있는지 궁금합니다.
-
해결됨김영한의 실전 데이터베이스 - 기본편
코드성 테이블과 캐시에 대한 질문
안녕하세요, 영한님. 다른 수강생 분의 질문 글에 남겨주신 답변에다가 제 질문을 작성했는데 혹시 이에 대한 알람은 가지 않을까봐 질문글을 따로 올리고, 아래에 그 링크를 첨부하도록 하겠습니다..!!(현재 글은 추후 자체 삭제 예정입니다)질문 글 링크https://inf.run/Rywjc
-
해결됨김영한의 실전 데이터베이스 - 기본편
실무에서 인덱스 설계는 보통 개발 어느 시점에 진행하나요?
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오) 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) 예[질문 내용]영한님이 제시해주신 가이드라인을 보면서'모든 기능 요구사항을 기본적으로 구현한 후, 작성된 쿼리를 전반적으로 훑어보면서 인덱스를 설계' 하는 것이 가장 효율적일 것이라고 생각이 들었는데요.영한님은 실무에서 인덱스 설계는 보통 개발 어떤 시점에 진행하시는지 궁금합니다.
-
해결됨스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판
gradle source sets가 없을때
안녕하세요 8강 강의에서 멀티모듈 구조 만드실때 각 서비스에 디렉토리를 추가하는 과정에서 new -> directory 클릭했을 때 강사님은 경로명 입력하는 칸 밑에 gradle source sets가 떠서 쉽게 src와 test디렉토리 등을 추가하셨는데 저는 뜨지 않습니다.. 왜 그런걸까요??
-
해결됨멀티 모듈 아키텍처로 구현하는 은행 서버 핵심 기능 [ Kotlin & Spring ]
모듈 간 의존성 관리에 대한 질문
안녕하세요. 강의 수강중에 의존성 추가에 대해서 의문이 들어 질문드립니다. 제가 26강까지 듣고 올리는 질문이라 후속 강의에서 리팩토링 되는 것이라면 넘어가주세요! 멀티모듈 프로젝트에서는 각 모듈이 각자의 의존성을 갖고 관리하도록 하는 것으로 이해하고 있었는데요. 현재 강의에서는 아래 의존성들이 여러 모듈에 흩어져있습니다. 이렇게 되면 의존성 관리 포인트가 늘어나 단점이 생길 것 같은데요. 하나의 모듈에서 각자의 역할과 책임에 맞게 의존성을 관리하는 것에 대해 강사님의 생각이 궁금합니다!spring data jpabank-domainbank-eventresilience4jbank-corebank-api만약 위의 의존성들이 각자의 모듈에서 관리된다면 다른 모듈에서 사용할 수 있도록 특정 기능들을 wrapping해서 제공해주는 방법이 맞을까요? 아니면 혹시 더 좋은 방법이 있을지 궁금합니다.spring data jpabank-domain에서 save(), 혹은 findById()를 다른 모듈에서 사용할 수 있도록 인자만 받는 메소드를 제공한다면 bank-event에선 id값 혹은 entity형식의 객체(필요시 model or dto 추가)만 넘겨 처리할 수 있으니 spring-data-jpa 의존성을 끊을 수 있을 것 같아서요.
-
해결됨은행 서버 프로젝트 실습을 통해 배우는 코틀린 마스터 클래스
data class 관련 질문입니다
강사님 안녕하세요!강의를 듣다가 궁금한 부분이 있어서 질문드립니다. 다름이 아니라, 검색을 해보면 코틀린에서 Entity 정의 시, data class는 권장하지 않고 사용하지 말라는 글들도 나오는데 강의에서는 data class를 사용하셔서 조금 혼란이 오는데요 ㅠㅠ 혹시 실제 코틀린을 사용하는 회사들은 Entity를 정의할 때, 일반 class가 아닌 data class를 사용하나요?
-
해결됨6주 완성! 백엔드 이력서 차별화 전략 4가지 - 똑같은 이력서 속에서 돋보이는 법
질문 있습니다
1. 현재 학습 진도몇 챕터/몇 강을 수강 중이신가요? 2-10 여기까지 이해하신 내용은 무엇인가요? 2. 어려움을 겪는 부분어느 부분에서 막히셨나요?코드의 어떤 로직이 이해가 안 되시나요?어떤 개념이 헷갈리시나요? 3. 시도해보신 내용문제 해결을 위해 어떤 시도를 해보셨나요?에러가 발생했다면 어떤 에러인가요?현재 작성하신 코드를 공유해주세요 강의 잘 보고 있습니다. 이전 강의에서 로컬이 아니라, 리소스가 제한된 클라우드 환경에서 성능 테스트를 하는 것이 좋다고 이해했습니다. 그런데 인텔리제이에서 제공하는 Profile이 기능은 클라우드 환경에서는 따로 사용할 수가 없지 않나요?? 해당 기능을 어떻게 클라우드 환경에서 애플리케이션 성능 측정을 위해 사용할 수 있을까요? 이렇게 구체적으로 알려주시면, 더 정확하고 도움이 되는 답변을 드릴 수 있습니다!
-
미해결김영한의 실전 데이터베이스 - 기본편
서브쿼리 메인쿼리 논리적 순서
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예[질문 내용]강의에서 서브쿼리가 먼저 실행된다고 설명해주셨는데, 그럼 메인쿼리 입장에서는 WHERE절이 먼저 실행되는거 아닌가요? 메인쿼리 입장에서는 FROM절 -> WHERE절 실행이니까 FROM먼저 실행되고, 그 다음에 서브쿼리(서브쿼리 내에서도 논리적 실행 순서 동일)가 실행되는건지 아니면 서브쿼리 즉 메인쿼리 입장에선 WHERE절이 먼저 실행되는건지 궁긍합니다.
-
해결됨6주 완성! 백엔드 이력서 차별화 전략 4가지 - 똑같은 이력서 속에서 돋보이는 법
동시성 테스트
1. 현재 학습 진도몇 챕터/몇 강을 수강 중이신가요? 4-8 여기까지 이해하신 내용은 무엇인가요? mysql 내부에서 자체적으로 공유락과 배타락을 사용 2. 어려움을 겪는 부분어느 부분에서 막히셨나요?코드의 어떤 로직이 이해가 안 되시나요?어떤 개념이 헷갈리시나요? 코드상에서 직접적으로 락을 사용하지 않아도 결론적으로 mysql 내부에서 락을 사용하는데 왜 동시성 테스트는 실패하는건가요?? 3. 시도해보신 내용문제 해결을 위해 어떤 시도를 해보셨나요?에러가 발생했다면 어떤 에러인가요?현재 작성하신 코드를 공유해주세요 이렇게 구체적으로 알려주시면, 더 정확하고 도움이 되는 답변을 드릴 수 있습니다!
-
미해결Real MySQL 시즌 1 - Part 1
ep12. (2) LEFT JOIN 사용 방법 준수 - 오타 질문
안녕하세요(2) LEFT JOIN 사용 방법 준수 - 오타 질문 (3:58 부근) 에서 첫 번째 SQL이 아래와 같이 써있는데요 select u.id, u.name, ua.value from user u left join user_attribute ua on ua.user_id=u.id where ua.name='address' where 조건에 ua.name이 아니고, ua.value가 아닐까 의미적으로 맞는게 아닌가 싶어서 질문드립니다. 전체 스키마가 있는게 아니다보니 제가 오해한 걸수도 있어서, user, user_attribute 테이블 전체 스키마를 주시면 감사하겠습니다!
-
해결됨Real MySQL 시즌 1 - Part 1
ep.12 count(*) 질문
안녕하세요, count(1) 대신 count(*)을 쓰라고 하셨는데요, count(1)이 성능이 더 좋다고 알고 있는데 제가 잘못 알고 있는 부분일까요? 성능적인측면에서도 답변 부탁드리면 감사하겠습니다~
-
해결됨6주 완성! 백엔드 이력서 차별화 전략 4가지 - 똑같은 이력서 속에서 돋보이는 법
Gradle Build 시 에러 발생
1. 현재 학습 진도몇 챕터/몇 강을 수강 중이신가요?2-7강 여기까지 이해하신 내용은 무엇인가요?git Action CI/CD 2. 어려움을 겪는 부분어느 부분에서 막히셨나요?git push 후 workflow에서 Gradle 빌드 실패코드의 어떤 로직이 이해가 안 되시나요?어떤 개념이 헷갈리시나요? 3. 시도해보신 내용문제 해결을 위해 어떤 시도를 해보셨나요?에러가 발생했다면 어떤 에러인가요?git push 후 workflow에서 Gradle 빌드 실패현재 작성하신 코드를 공유해주세요 빌드를 진행할 때 먼저 3개의 테스트에서 실패합니다.SimpleEventListenerTest > 현재 코드 구조에서 @TransactionalEventListener는 작동하지 않는다 FAILED java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:143 Caused by: org.springframework.beans.factory.BeanCreationException at AbstractAutowireCapableBeanFactory.java:1770 Caused by: jakarta.persistence.PersistenceException at AbstractEntityManagerFactoryBean.java:421 Caused by: org.hibernate.exception.JDBCConnectionException at SQLExceptionTypeDelegate.java:49 Caused by: java.sql.SQLNonTransientConnectionException at SQLError.java:111 SimpleEventListenerTest > @TransactionalEventListener는 활성 트랜잭션이 필요하다 FAILED java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:143 Caused by: org.springframework.beans.factory.BeanCreationException at AbstractAutowireCapableBeanFactory.java:1770 Caused by: jakarta.persistence.PersistenceException at AbstractEntityManagerFactoryBean.java:421 Caused by: org.hibernate.exception.JDBCConnectionException at SQLExceptionTypeDelegate.java:49 Caused by: java.sql.SQLNonTransientConnectionException at SQLError.java:111 SimpleEventListenerTest > @EventListener는 트랜잭션이 없어도 정상 동작한다 FAILED java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:143 Caused by: org.springframework.beans.factory.BeanCreationException at AbstractAutowireCapableBeanFactory.java:1770 Caused by: jakarta.persistence.PersistenceException at AbstractEntityManagerFactoryBean.java:421 Caused by: org.hibernate.exception.JDBCConnectionException at SQLExceptionTypeDelegate.java:49 Caused by: java.sql.SQLNonTransientConnectionException at SQLError.java:111이후, Redis에 Reconnecting 하려고 시도하지만 Connection이 Refused되면서 빌드에 실패합니다.2025-08-21T08:34:32.560Z INFO 2235 --- [xecutorLoop-1-2] i.l.core.protocol.ConnectionWatchdog : Reconnecting, last destination was localhost/127.0.0.1:32770 2025-08-21T08:34:32.570Z WARN 2235 --- [ioEventLoop-8-2] i.l.core.protocol.ConnectionWatchdog : Cannot reconnect to [localhost/<unresolved>:32770]: Connection closed prematurely io.lettuce.core.RedisConnectionException: Connection closed prematurely at io.lettuce.core.protocol.RedisHandshakeHandler.channelInactive(RedisHandshakeHandler.java:91) ~[lettuce-core-6.2.6.RELEASE.jar:6.2.6.RELEASE] // 생략로컬 환경과 AWS EC2 환경에서 동일하게 발생합니다. 해결 방법gradle build를 5번 정도 시도했는데, 5번 중 4번은 SimpleEventListenerTest에서 테스트가 실패하고, 1번은 다른 Test에서 실패하였습니다.그래서, SimpleEventListenerTest에 대해 개별 테스트를 진행해보니 통과하였습니다.전체 테스트 진행에서는 JDBC Connection 관련 오류가 발생했고, 개별 테스트 진행에서는 문제가 발생하지 않는다는 점에서 커넥션 풀 설정 문제를 의심하였습니다. spring: datasource: url: jdbc:mysql://${RDS_ENDPOINT:localhost}:3306/portfolio?useSSL=false&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true&useSSL=false&rewriteBatchedStatements=true username: portfolio_user password: ${RDS_PASSWORD:portfolio1234} driver-class-name: com.mysql.cj.jdbc.Driver hikari: maximum-pool-size: 32@TestConfiguration public class TestDatabaseConfig { @Container private static final MySQLContainer<?> mysqlContainer;application.yml 의 최대 풀 사이즈는 32로 설정되어 있고, mySQL 컨테이너는 전역으로 설정되어 있으므로 병렬 처리 과정에서 커넥션 풀이 부족할 수 있겠다는 판단하에, maximum-pool-size를 100으로 늘려보았습니다.package ding.co.backendportfolio.config; import jakarta.annotation.PreDestroy; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.context.annotation.Bean; import org.testcontainers.containers.MySQLContainer; import org.testcontainers.junit.jupiter.Container; @TestConfiguration public class TestDatabaseConfig { @Container private static final MySQLContainer<?> mysqlContainer; static { mysqlContainer = new MySQLContainer<>("mysql:8.0.33") .withDatabaseName("testdb") .withUsername("test") .withPassword("test"); mysqlContainer.start(); // A) 기본 설정 + rewriteBatchedStatements 옵션 String originalJdbcUrl = mysqlContainer.getJdbcUrl() + "?rewriteBatchedStatements=true"; // TODO: BulkInsert 모니터링 - 아래 주석을 해제해야함 // originalJdbcUrl = originalJdbcUrl + "&profileSQL=true&logger=Slf4JLogger&maxQuerySizeToLog=2147483647"; System.setProperty("spring.datasource.url", originalJdbcUrl); System.setProperty("spring.datasource.username", mysqlContainer.getUsername()); System.setProperty("spring.datasource.password", mysqlContainer.getPassword()); // 커넥션 풀 늘리기 System.setProperty("spring.datasource.hikari.maximum-pool-size", "100"); System.setProperty("spring.datasource.hikari.minimum-idle", "10"); } @Bean public MySQLContainer<?> mySQLContainer() { return mysqlContainer; } @PreDestroy public void stop() { if (mysqlContainer != null && mysqlContainer.isRunning()) { mysqlContainer.stop(); } } } 이후 테스트를 진행해보니, 기존에 작성해두신 커넥션 풀 테스트인 EventJoinWithExternalConnectionPoolTest를 제외하고 테스트에 통과하였습니다.따라서, EventJoinWithExternalConnectionPoolTest이 테스트에만 작은 커넥션 풀을 적용하도록 하였습니다.@Slf4j @IntegrationTest // 커넥션 설정 @TestPropertySource(properties = { "spring.datasource.hikari.maximum-pool-size=32", "spring.datasource.hikari.minimum-idle=5" }) class EventJoinWithExternalConnectionPoolTest {문제가 발생한 원인은, Gradle 병렬 테스트 진행 시 커넥션 풀이 부족하여 커넥션이 이뤄지지 않았던 것 같습니다.사실 최대 커넥션 풀을 100개로 늘리는 건 임시방편인 것 같고, 테스트별로 독립적인 컨테이너 환경을 만들어주는게 좋을 것 같다고 생각합니다. P.S.) GPT, 클로드코드, Cursor, ... 다 문제 원인을 파악하지 못하더라고요 ㅎㅎ
-
해결됨김영한의 실전 데이터베이스 입문 - 모든 IT인을 위한 SQL 첫걸음(SQL부터 차근차근)
문제5번 - 함수 코드 중복 방지를 위한 방법 중 서브쿼리와 CTE의 차이
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)[질문 내용]질문이 서론과 본론으로 구분되어 있으며, 실제 질문 내용은 본론에 있습니다! 서론안녕하세요 영한님.문제5번에서는 SUBSTRING_INDEX(email, '@', 1) 함수를 user_id와 id_length에서 두 번 반복해서 사용하게 됩니다.중복 문제를 개선하기 위해 초기에는 다음과 같이 CHAR_LEGNTH(user_id) AS id_length로 시도를 했으나 실패했습니다. -- 에러 발생 코드 SELECT email, SUBSTRING_INDEX(email, '@', 1) AS user_id, CHAR_LENGTH(SUBSTRING_INDEX(user_id) AS id_length FROM customers ;그 이유는 SELECT 절의 모든 표현식이 논리적으로 동시에 평가되기 때문이라고 판단했고, 이를 해결하기 위한 방법 중 서브쿼리와 CTE(Common Table Expression) 방식을 찾았고 이를 직접 적용해봤습니다. -- sol1. 서브 쿼리 SELECT email, user_id, CHAR_LENGTH(user_id) AS id_length FROM (SELECT email, SUBSTRING_INDEX(email, '@', 1) AS user_id FROM customers ) AS dt ;-- sol2. CTE WITH email_parts AS ( SELECT email, SUBSTRING_INDEX(email, '@', 1) AS user_id FROM customers ) SELECT email, user_id, CHAR_LENGTH(user_id) AS id_length FROM email_parts; 본론이 과정에서 "서브 쿼리와 CTE의 성능 차이가 있는가?"에 대한 의문이 들어서 리서치를 했으나 제 수준이 부족한 탓인지 이에 대한 명확한 답변을 찾지 못했는데요(현재 '상황에 따라 다르다' 수준으로만 이해한 상태입니다).혹시 실무에서는 서브 쿼리와 CTE 중 (성능, 가독성, 개발팀 관례 등의 이유로 인해) 무엇을 선호하는지 의견을 들을 수 있을까 싶어 질문 남깁니다!혹시 다음 강의에서 다루는/다룰 내용이라면 제가 추후에 해당 강의를 들을 때 학습하도록 하겠습니다!
-
미해결김영한의 실전 데이터베이스 - 기본편
중복제거
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문 전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]풀이 시간에SELECT distinct u.name, u.email FROM users u JOIN orders o on u.user_id = o.user_id WHERE o.quantity > 1;한 번의 주문으로 상품을 2개 이상 구매한 고객의 쿼리는 중복제거를 해주셨는데요,SELECT u.name AS 고객명, u.email AS 이메일 FROM users u JOIN orders o on u.user_id = o.user_id JOIN products p on o.product_id = p.product_id WHERE p.category = '전자기기''전자기기' 카테고리 상품을 한 번이라도 구매한 고객은 중복제거를 해주지 않았더라구요. 하나의 그룹에 고객이 두명이 나오는 게 이상해서이 경우 distinct 혹은 GROUP BY u.name, u.email을 추가해주어 중복제거 해주면 될까요? distinct 와 GROUP BY 중 어떤걸 사용하는 게 더 좋을까요? 집계함수를 사용하지 않으니 distinct가 더 나을까요?