services: springboot-twitter: container_name: springboot-twitter image: springboot-twitter:latest ports: - "8080:8080" environment: - SPRING_DATASOURCE_URL=jdbc:mysql://mysql-twitter:3306/twitterdb - SPRING_DATASOURCE_USERNAME=dev - SPRING_DATASOURCE_PASSWORD=dev123 networks: - twitter-network depends_on: mysql-twitter: condition: service_healthy mysql-twitter: container_name: mysql-twitter image: mysql:9.3 environment: MYSQL_ROOT_PASSWORD: root123 MYSQL_DATABASE: twitterdb MYSQL_USER: dev MYSQL_PASSWORD: dev123 healthcheck: test: [ "CMD", "mysqladmin", "ping", "-h", "localhost" ] interval: 10s timeout: 5s retries: 5 ports: - "3307:3306" networks: - twitter-network networks: twitter-network: driver: bridge 로컬 환경에서 mysql에 대한 이미지가 떠있어야 spring boot application을 실행해야 DB connection도 잘 이루어진다고 이해했습니다. 여기서 궁금한 점이 있습니다. 로컬 환경에서 docker compose up -d를 한 경우 compose 파일에서 spring boot 이미지도 함께 띄워놔서 application을 실행할 때 8080 already in use 오류가 발생합니다. 이렇게 될 경우에는 docker-compose.yaml을 수정하거나 docker compose up -d mysql-twitter 커맨드를 실행해서 mysql만 띄워야 하는지 궁금합니다.
docker buildx build --platform linux/amd64 -t {이미지 이름} . 해당 명령어를 통해서 Dockerfile 기반으로 Docker Hub에 컨테이너를 생성하고, Docker Compose를 통해서 생성된 컨테이너 안에 이미지들을 다운받아와서 실행되는 구조가 맞는걸까요 ??
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요. 1. 강의 내용과 관련된 질문을 남겨주세요. 2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요. (자주 하는 질문 링크: https://bit.ly/3fX6ygx) 3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요. (질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG) 질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요. ========================================= [질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예/아니오) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) [질문 내용] 여기에 질문 내용을 남겨주세요. 안녕하세요 코드를 따라 치는 과정에서 아래와 같이 작성 했더니 BeanCreationException 이 발생하는 것을 확인 할 수 있었습니다. public void dbInit1(){ Member member = new Member(); member.setName("userA"); member.setAddress(new Address("서울", "1", "111")); em.persist(member); Book book = new Book(); book.setName("JPA1 BOOK"); book.setPrice(10000); book.setStockQuantity(100); em.persist(book); Book book2 = new Book(); book.setName("JPA2 BOOK"); book.setPrice(20000); book.setStockQuantity(100); // book2.setName("JPA2 BOOK"); // book2.setPrice(20000); // book2.setStockQuantity(100); em.persist(book2); OrderItem orderItem1 = OrderItem.createOrderItem(book, 10000, 1); OrderItem orderItem2 = OrderItem.createOrderItem(book2, 20000, 2); Delivery delivery = new Delivery(); delivery.setAddress(member.getAddress()); em.persist(delivery); Order order = Order.createOrder(member, delivery, orderItem1, orderItem2); em.persist(order); } 이때 위의 코드에서 주석 처리된 부분처럼 고쳤더니 정상적으로 코드가 실행되는 것을 확인했습니다. 그런데 이게 어떤 문제에서 오류가 발생한건지 정확히 알 수 없어 질문드립니다. 처음에는 book2의 값이 아무것도 없어서 null에 관한 엔터티 제약조건 관련해서 문제가 발생했나 생각해지만 notnull 같은 제약조건이 없었고 Book이 Item과 상속관계이고 싱글 테이블 전략을 사용해서 DTYPE 값이 book2 생성시 자동으로 생성 된다고 기억해서 특별히 값이 비었다고 생기는 오류가 아니라고 생각이 들었습니다. 결론적으로는 정확히 어떤 원인에서 발생한 문제인지 궁금해서 질문 남깁니다.
@Configuration public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) { return http .authorizeHttpRequests(auth -> auth.anyRequest().authenticated()) .formLogin(Customizer.withDefaults()) .authenticationProvider(customAuthenticationProvider()) .authenticationProvider(new DaoAuthenticationProvider(inMemoryUserDetailsManager())) .build(); } @Bean public AuthenticationProvider customAuthenticationProvider() { return new CustomAuthenticationProvider(); } @Bean public UserDetailsService inMemoryUserDetailsManager() { UserDetails user = User.withUsername("user") .password("{noop}1111") .roles("USER") .build(); return new InMemoryUserDetailsManager(user); } } 메소드 인자로 AuthenticationManagerBuilder 랑 Authenticationconfiguration 을 주입 받아서 수동으로 parent Manager가 가진 Provider를 삭제하고 추가하는 작업을 하지 말고, 그냥 안전하게, 대체되었던 DaoAuthenticationProvider 를 authenticationProvider() 로 다시 추가하면 안 되나요?
@Test @DisplayName("하위 댓글이 삭제되고, 삭제되지 않은 부모면, 하위 댓글만 삭제한다.") void deleteShouldDeleteChildrenOnlyIfNotDeletedParent() { // given Long articleId = 1L; Long commentId = 2L; Long parentCommentId = 1L; Comment comment = createComment(articleId, commentId, parentCommentId); given(comment.isRoot()).willReturn(false); Comment parentComment = mock(Comment.class); given(parentComment.getDeleted()).willReturn(false); given(commentRepository.findById(commentId)) .willReturn(Optional.of(comment)); given(commentRepository.countBy(articleId, commentId, 2L)).willReturn(1L); // 이 부분 given(commentRepository.findById(parentCommentId)) .willReturn(Optional.of(parentComment)); // ... 위 테스트에서 given(commentRepository.countBy(articleId, commentId, 2L)).willReturn(1L); 이 부분 때문에 질문을 드렸습니다. commentRepository.countBy() 에서 commentId = 2L 을 부모로 갖는 comment는 아무도 없으므로, 0L 을 반환하는 것이 옳다고 이해했는데, 강의에선 1L 을 반환하셨습니다! (물론 서비스 계층의 메서드에서 hasChildren() 의 판정 로직상 0L 과 1L 은 동일한 결과를 반환하므로 테스트 결과에는 영향을 주지 않는 것 같습니다.) 카운트 쿼리의 동작 부분을 제가 잘못 이해한 것인지 질문드립니다.
[질문 내용] 기존 링크 https://github.com/spring-projects/spring-boot/blob/main/spring-boot-project/spring-boot-dependencies/build.gradle 변경된 링크 https://github.com/spring-projects/spring-boot/blob/main/platform/spring-boot-dependencies/build.gradle 다음 링크로 변경이 필요할 거 같습니다.
안녕하세요 실무에서 '관리자(Admin) 예약 변경 기능' 정책을 두고 기획팀과 이견이 있어, 실무에서는 어떤 방식이 범용적인지 여쭙고 싶습니다. [시스템 상황] 유저가 예약할 때 다양한 할인(이벤트, 타임세일, 쿠폰, 기업지원 등)이 적용되며, 이 정보는 예약 시점에 스냅샷(Snapshot) 으로 저장됩니다. 현재 어드민(상담원/운영팀) 이 유저의 예약 시간/날짜를 변경하는 기능을 개발 중입니다. [이슈 사항] 기획상으로는 어드민에서 시간을 변경할 때도 모든 할인 조건을 '실시간'으로 재검증 하라고 합니다. 문제는 재검증 과정에서 '쿠폰 박탈' 같은 상황이 발생한다는 점입니다. 예시 상황: 유저가 5만원 짜리 예약에 5만원 이상 결제 시 사용 가능한 10% 쿠폰 을 씀. 어드민이 사정상(또는 유저 요청으로) 가격이 저렴한 타임이나 옵션으로 변경함 -> 결제액이 4만원 이 됨. 기획 요구사항: "최소 결제 금액(5만원) 조건을 불만족하게 되었으니, 자동으로 쿠폰 적용을 해제(원복)하고 금액을 재계산 한다." [제(개발자) 의견 및 고민] 저는 위 기획이 어드민 기능의 목적 과 UX(고객 경험) 에 맞지 않는다고 생각합니다. 고객 경험 훼손: 유저는 단지 시간을 바꿨을 뿐인데, 시스템이 엄격하게 검증해서 "조건 미달이니 쿠폰 뺏어가겠습니다"라고 하면 컴플레인 요지가 다분합니다. (유저 입장에선 혜택 유지를 원하니까요.) 데이터 복잡도: 이미 스냅샷으로 저장된 할인 정보를, 수정 시점에 다시 현재 기준의 마스터 데이터(쿠폰 유효기간, 최소금액 등)와 대조해서 '줬다 뺏는' 로직을 짜는 건 구현 복잡도 대비 실익이 너무 적습니다. 관리자의 재량: 어드민에서의 변경은 보통 '강제성(Override)' 을 띠는 경우가 많은데, 시스템이 칼같이 혜택을 잘라버리는 게 맞나 싶습니다. [질문] 보통 예약 도메인에서 관리자(Admin)가 개입하여 예약을 변경할 때 도, 이렇게 엄격하게 유저의 할인 자격(최소금액, 유효기간 등)을 재검증하여 박탈 시키는 게 맞나요? 아니면 어드민 권한 변경인 경우 "기존 스냅샷(혜택)을 최대한 유지" 해주거나, 가격 변동이 불가피하면 "취소 후 재예약" 을 하는 프로세스가 더 일반적인가요? 개발자로서 이 복잡한 '조건부 쿠폰 회수' 로직을 방어하고 싶은데, 설득력 있는 논리가 필요합니다. 조언 부탁드립니다!
섹션 19 부분은 예전에 강의하셨을 때 쓰신 스프링부트 2점대 버전을 3점대 버전으로 업그레이드하는 강의인가요? 최신 강의 기준 업데이트 하신 3점대 버전으로 강의를 듣고 있는데 섹션 19에서 강의가 정확히 어떤 강의인지 궁금합니다. 만약 2점대 버전을 3점대 버전으로 업그레이드 하는 강의라면, 섹션 19에서 분산 트레이싱, swagger, kubernates 부분만 들어도 되는걸까요?
현재 백엔드 프로젝트에서 스프링 시큐리티 프레임워크 도움을 받기 위해 CSRF 설정은 끄고 <form> 전송과 세션 방식으로 로그인 하고 있잖아요. (저는 그렇게 보였는데 아니면 제가 잘못 이해한거구요..) 그런데 요즘은 SSR 방식이 아닌 현재 프로젝트처럼 프론트 따로 서버 따로 해서 서버는 REST API 만 개발하고 JWT 방식을 쓰잖아요 JWT 도 보안에 한계가 있는데 아무튼 이걸 떠나서 현재 CSRF 설정을 끄고 <form> 전송으로 로그인하는게 보안상 많이 위험한 건 아닌가요? 제가 애초에 잘못 이해하고 있어서 질문 자체가 잘못되었을 수 있지만 관련해서 답변 부탁드립니다
MemberDetail 테이블의 UK_MEMBER_DETAIL_PROFILE_ADDRESS 유니크 제약 조건과 관련해서 질문이 있습니다. 39장 강의 마지막 부분에서, 해당 프로파일 주소(profile_address)를 빈 문자열로 바꿔 삭제할 수 있는 부분 테스트를 추가 하셨는데, 그런데 이렇게 되면 여러 사용자가 프로필 주소를 빈값으로 변경 할 경우 제약 조건에 충돌이 발생할 수 있을 것 같습니다. profile_address 값을 빈값으로 설정할 수 없도록 테스트를 조정하는게 더 맞아 보이는데 제가 생각한게 맞을까요?
킬구형 내가 그동안 보면서 정리한내용으로, 실습을 진행해보려고 혼자 스스로 파일을 만들었어. 실습 환경은 h2 데이터 베이스로 테스트 했고, 메타테이블은 자동으로 만들어졌어. 간단한 스텝과 잡을 만들어서 콘솔에서 잘 실행되는것 까지는 확인을 했는데 실제 데이터 베이스 테이블인 batch_job_instance와 같은 테이블에는 데이터가 들어가지 않아. 혹시 몰라서 db url도 확인해봤는데 정확하게 일치하고.. 이런경우는 어디를 확인해보면 제일 좋을까..? 콘솔에서 에러도 나지 않고 정상적으로 배치가 실행돼서 답답해 미치겠어 ㅠㅠㅠ 콘솔에서 insert 쿼리는 안나가고 있는데, 쿼리가 왜 안나가는지를 도통 모르겠어 ㅠㅠ 다 잘되는데 메타테이블에만 데이터가 들어가지 않네.. 참고로 tasklet 방식으로 테스트 해봤어. 부트 4.0.1 버전 사용하고 있어 킬구형 강의자료 에서도 중간에 postgresql로 전환하던데 혹시 h2는 뭔가 지원을 하지 않는걸까?
먼저 gpt를 통해 elk 스택을 설치했습니다. discover를 클릭하니 아래의 이미지와 맞지 않은 결과가 나오는데.. 상위버전이나 혹은 하위버전처럼 나오는경우 어떻게하면 좋을까요? http://localhost:5601/app/management/kibana/indexPatterns/ 으로 이동하게되는데.. create dataview는 어떤 경로로 나올수있을까요? 아래의 docker정보는 실제 제가 설치한 정보입니다. Index patterns이 나옵니다.
안녕하세요, 무한 스크롤 방식에서 페이지 번호 방식 쿼리의 문제점을 설명한 부분에 질문이 있습니다. 페이지 방식을 무한 스크롤 방식에 적용 시 문제점으로 아래 2가지를 제시하셨는데요, 1) 페이지가 추가된 경우, 데이터 중복 조회 문제 2) 페이지가 삭제된 경우, 데이터 누락 문제 궁금한 점은, 이러한 문제는 무한 스크롤에서 사용했을 때 뿐 아니라, 페이지 번호 방식을 사용할 때도 마찬가지로 문제가 있는 것 아닌가 싶다는 것입니다. 페이지 번호 방식 자체의 한계라고 생각하는데, 페이지 번호 방식에서는 발생하지 않는 문제인가요?
안녕하세요! 수업 잘 듣고 있습니다. 사소한 내용이긴한데.. OrderService.create 에서 OrderKeyGenerator 객체를 를 주입받아 generate() 를 호출해서 orderKey 를 생성하는 부분에서 OrderKeyGenerator class 는 상태를 가지지도 않고 Property 를 가지지도 않는데 static method, 함수로 구현 되어도 되지 않았을까 하는 생각이 들었는데요. 어떤 고려사항이 있는지 궁금합니다. static 이든 함수든 객체 메서드이든 상관 없다? 나중에 OrderKeyGenerator 가 확장되는 것이 고려된 것? OrderSerivce 에서 사용하는 기능이니 주입되는 것이 더 응집되어 보여서 더 좋다? 그냥 함수를 가져다 쓰는 것보다 객체를 주입하는 쪽이 테스트 하기 좋다? generate 가 순수함수가 아니라서? class OrderKeyGenerator { fun generate(): String { return Base64.getUrlEncoder().withoutPadding().encodeToString( ByteBuffer.allocate(16).apply { UUID.randomUUID().also { putLong(it.mostSignificantBits) putLong(it.leastSignificantBits) } }.array(), ) } }
강사님 강의 재밌게 너무 잘 보고있습니다!!! 클로드 코드에 대해서도 사용해보니까.. 재밌어요 PostgreSQL을 왜 선택하고 특징, 장점까지 잘 보았습니다. 하지만 MySQL에 유리한 프로젝트는 무엇인지 궁금합니다. PostgreSQL선택한 이유중에 부분 인덱스 지원해주고 JSON을 효율적으로 저장할 수 있는 타입인 JSONB도 지원해주고 커뮤니티도 풍부하고 READ COMMITTED - 높은 읽기 성능을 제공해주는것으로 MySQL보다 전부 뛰어나다고 생각이듭니다. MySQL과 차이점은 MySQL은 Reapeatable Read로 정합성을 챙기고 MVCC덕분에 높은 읽기 성능을 가지고 있는걸로 알고있습니다. 그 외에 MySQL의 장점과 유리한 프로젝트들 예시를 좀 조언을 얻고싶습니다