묻고 답해요
169만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
해결됨스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판
게시글 테스트 데이터 삽입 에서 SQL 최대 2만건 삽입 되는문제
게시글 데이터 삽입 부분에서요, 영상 강의에서는 데이터 1200만건 삽입 하는데 대략 13분 정도 걸리는걸 확인 햇는데, 저는 2초만에 끝나더니 삽입된 갯수 확인해보니까 2만건만 추가 되고 더 추가 안된거같은데 어떤 문제가 있을가요??아래는 코드랑 영상에서 설명한 sql 설정값 첨부 했습니다. @SpringBootTest public class DataInitializer { @PersistenceContext EntityManager entityManager; @Autowired TransactionTemplate transactionTemplate; Snowflake snowflake = new Snowflake(); CountDownLatch latch = new CountDownLatch(EXECUTE_COUNT); static final int BULK_INSERT_SIZE = 2000; static final int EXECUTE_COUNT = 6000; @Test void initialize() throws InterruptedException { ExecutorService executorService = Executors.newFixedThreadPool(10); for(int i = 0; i < EXECUTE_COUNT; i++) { executorService.submit(() -> { insert(); latch.countDown(); System.out.println("latch.getCount() = " + latch.getCount()); }); } latch.await(); executorService.shutdown(); } void insert() { transactionTemplate.executeWithoutResult(status -> { for(int i = 0; i < BULK_INSERT_SIZE; i++) { Article article = Article.create( snowflake.nextId(), "title" + i, "content" + i, 1L, 1L ); entityManager.persist(article); } }); } }
-
해결됨웹소켓/STOMP 채팅서비스(spring, vue, redis)
섹션6. '채팅메시지 저장' 강의에서 질문 있습니다
안녕하세요. 선생님섹션6. '채팅메시지 저장' 강의에서 질문 있습니다.7:50 부터 사용자별로 읽음 여부 저장을 구현하실때chatParticipantRepository에서 chatRoom을 통해 participants list를 가져오도록 구현하셨는데 조금 다르게 하고 싶어서 질문 드립니다.저희 엔티티 구성할때 ChatRoom entity에 <List>chatParticipants 필드를 만들었으니까 여기서 해당 채팅 룸의 participants를 가져와도 상관이 없을까요 ?
-
해결됨스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판
댓글 신규 path 동시성 이슈
댓글의 path를 설정하는 과정에서 public CommentPath createChildCommentPath(String descendantsTopPath) { if (descendantsTopPath == null) { return CommentPath.create(path + MIN_CHUNK); } String childrenTopPath = findChildrenTopPath(descendantsTopPath); return CommentPath.create(increase(childrenTopPath)); } 이런식으로 findChildrenTopPath를 설정하게 되는데 이 과정에서 동시성 이슈가 발생할 수 있을 것 같아 질문 드립니다. increase하는 함수에서도 동시성 제어를 하는 파트가 없어 동시에 같은 계층의 댓글이 생성되면 id가 겹칠 것 같습니다!
-
미해결스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
jpa 책 질문있습니다.
강의에서 2015년에 나왔다고 하셨던거 같은데지금 책 구매해서 봐도 괜찮나요?에러라던가 버전이라던가 아니면 jpa 방식이 변했다던가 그런부분은 거의 없나요 ?
-
해결됨RabbitMQ를 이용한 비동기 아키텍처 한방에 해결하기
알림은 왜 pub sub 구조로 설계하나요?
동일한 메시지를 여러 서비스에서 처리하는 것을 pub sub 구조로 이해하였는데요,알림이라는 하나의 서비스에서 처리한다면 pub sub 구조로 설계할 필요가 없는 걸까요?
-
해결됨스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판
인기글 Consumer 구현 - 이벤트 핸들러 및 서비스 레이어 강의 질문
학습 관련 질문을 최대한 상세히 남겨주세요!고민 과정도 같이 나열해주셔도 좋습니다.먼저 유사한 질문이 있었는지 검색해보세요.인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요.해당 강의 마지막 부분의 handleEventIfScoreUpdatedEventTest()에서given(event.getType()).willReturn(mock(EventType.class));위의 코드로 test하신 이유가 단순히 게시글 생성과 게시글 삭제만 아니면 되어서,그냥 아무 class로 테스트하신게 맞으실까요?? 아래 코드로도 가능하긴 하지만 그냥 게시글 생성과 게시글 삭제만 아니면, test 가능하니로 이해하면 될까요?given(event.getType()).willReturn(EventType.ARTICLE_UPDATED);
-
미해결스프링 시큐리티 OAuth2
OAuth2AuthorizedClient 이해 및 활용 강의 내용 질문
1 의문 ................................................강의 내용중에서oauth2Login(Customizer.withDefaults())없이oauth2Client(Customizer.withDefaults())만 사용한 경우에는 인가 처리만 하기 때문에 따로 인증 처리를 해주는 과정을 보여주셨는데 2 배경지식 ................................................oauth2Client 와 oauth2Login 의 차이는 1단계 처리 필터 : OAuth2AuthorizationRequestRedirectFilter 로 동일2단계 를 처리필터: OAuth2LoginAuthenticationFilter 대신 여기선 OAuth2AuthorizationCodeGrantFilter 사용 즉OAuth2LoginAuthenticationFilter 를 거치지않아OAuth2LoginAuthenticationFilter에서 사용하는OidcAuthorizationCodeAuthenticationProvider에서IdToken 을 추출해OidcIdToken를 만들고 OidcUser 를 로드하는 부분의 유무가 핵심적인 차이라고 생각이드는데요 그리고 OAuth2User는 인가 개념만 있지 인증 개념은 없다고 들었습니다 3 질문 ....................................................... 단순히 OAuth2User를 만들어 OAuth2AuthenticationToken을 SecurityContextHolder에 수동 저장한다고 해서 "인증된 사용자"로 간주되는 않아서로그아웃도 제대로 동작하지 않습니다 .물론 스프링 시큐리티 자체에서 세션,쿠키,SecurityContextHolder에 강제로 저장한 Authentication 없애기를 통해 일부는 처리는 되지만질문1 : keycloak 에서의 실제 세션은 로그아웃을 못하지않나요 ?@GetMapping("/logout") public String logout( HttpServletRequest request, HttpServletResponse response){ Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); // 로그아웃 처리 SecurityContextLogoutHandler logoutHandler = new SecurityContextLogoutHandler(); logoutHandler.logout(request, response, authentication); // 로그아웃 요청을 위한 Keycloak 로그아웃 URL String logoutUrl = "http://localhost:8080/realms/oauth2/protocol/openid-connect/logout"; // 로그아웃 후 리디렉션할 URI String redirectUri = "http://localhost:8081/login"; // 로그아웃 후 돌아올 URL // 실제 ID Token을 가져오는 코드 // 로그아웃 URL에 필요한 파라미터들을 추가 String logoutRequestUrl = logoutUrl + "?id_token_hint=" + idTokenHint + "&post_logout_redirect_uri=" + redirectUri; // 로그아웃 요청을 Keycloak으로 리디렉션 return "redirect:" + logoutRequestUrl; }이런식으로 강제로 형식 만들려고 해도 mvc 에서 id_token을 참조할 수 있는 방법이없습니다 .디버깅으로 확인해보니scope 에 openId 를 추가해서 id_token이 있을거라고 생각했는데OAuth2AuthorizationCodeGrantFilter의 OAuth2AuthorizationCodeGrantFilter에서 authenticationResult 에 additionalParameters = {HashMap@7785} size = 4 "id_token" -> "ㅁㄴㅇㄹㄴㅇㅁㄻㄴㅇㅁㄴㅇㄹ" "session_state" -> "7a3b7dbb-ec62-4a07-8ef8-f67b17b2f3ac" "refresh_expires_in" -> {Integer@7818} 1800 "not-before-policy" -> {Integer@7820} 1746349855이런식으로 있긴하지만 사용하지않고 authenticationResult로부터 authenticationResult.getClientRegistration() 추출해서 OAuth2AuthorizedClient 만들어서 저장하고 끝내내요 private void processAuthorizationResponse(HttpServletRequest request, HttpServletResponse response) throws IOException { OAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestRepository.removeAuthorizationRequest(request, response); String registrationId = (String)authorizationRequest.getAttribute("registration_id"); ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(registrationId); MultiValueMap<String, String> params = OAuth2AuthorizationResponseUtils.toMultiMap(request.getParameterMap()); String redirectUri = UrlUtils.buildFullRequestUrl(request); OAuth2AuthorizationResponse authorizationResponse = OAuth2AuthorizationResponseUtils.convert(params, redirectUri); OAuth2AuthorizationCodeAuthenticationToken authenticationRequest = new OAuth2AuthorizationCodeAuthenticationToken(clientRegistration, new OAuth2AuthorizationExchange(authorizationRequest, authorizationResponse)); authenticationRequest.setDetails(this.authenticationDetailsSource.buildDetails(request)); OAuth2AuthorizationCodeAuthenticationToken authenticationResult; try { authenticationResult = (OAuth2AuthorizationCodeAuthenticationToken)this.authenticationManager.authenticate(authenticationRequest); } catch (OAuth2AuthorizationException var16) { OAuth2AuthorizationException ex = var16; OAuth2Error error = ex.getError(); UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(authorizationRequest.getRedirectUri()).queryParam("error", new Object[]{error.getErrorCode()}); if (StringUtils.hasLength(error.getDescription())) { uriBuilder.queryParam("error_description", new Object[]{error.getDescription()}); } if (StringUtils.hasLength(error.getUri())) { uriBuilder.queryParam("error_uri", new Object[]{error.getUri()}); } this.redirectStrategy.sendRedirect(request, response, uriBuilder.build().encode().toString()); return; } Authentication currentAuthentication = this.securityContextHolderStrategy.getContext().getAuthentication(); String principalName = currentAuthentication != null ? currentAuthentication.getName() : "anonymousUser"; OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(authenticationResult.getClientRegistration(), principalName, authenticationResult.getAccessToken(), authenticationResult.getRefreshToken()); this.authorizedClientRepository.saveAuthorizedClient(authorizedClient, currentAuthentication, request, response); String redirectUrl = authorizationRequest.getRedirectUri(); SavedRequest savedRequest = this.requestCache.getRequest(request, response); if (savedRequest != null) { redirectUrl = savedRequest.getRedirectUrl(); this.requestCache.removeRequest(request, response); } this.redirectStrategy.sendRedirect(request, response, redirectUrl); }질문 2 : 강의(OAuth2AuthorizedClient 이해 및 활용)에서는OAuth2User를 만들어 OAuth2AuthenticationToken 을 저장하는 식으로 처리하셨는데 수업을 듣다 보니 이렇게 처리한 이유가 궁금해 질문 드립니다 .
-
미해결실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
OrderService의 .cancel() 메서드에 대해 질문드립니다.
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]이 부분 관련해서 반복적으로 학습중인데 이해가 잘 되지 않아 질문드립니다! /* 취소 */ @Transactional public void cancelOrder(Long orderId) { //주문 엔티티 조회 Order order = orderRepository.findOne(orderId); //주문취소 order.cancel(); }이 부분에서 orderId"주문 PK" 를 인자로 받아 주문에 대한 데이터 order 를 조회해왔습니다.그다음 order.cancel(); 메서드를 호출하는데해당 메서드에는 파라미터로 아무것도 넣지 않았습니다. //==비즈니스 로직==// /** * 주문 취소 */ public void cancel(){ if (delivery.getStatus() == DeliveryStatus.COMP) { throw new IllegalStateException("이미 배송완료된 상품은 취소가 불가능합니다."); } this.setStatus(OrderStatus.CANCEL); for (OrderItem orderItem : orderItems) { orderItem.cancel(); } }그 다음 위의 Order 엔티티의 cancel 메서드가 호출이 되는데예외처리 부분을 지나 이제"해당 주문에 포함된(저장된) 주문품목(OrderItem) 의 addStock() 하는 메서드까지 연결이 됩니다.OrderService > Order > OrderItem > Item 까지의 순차적인 cancel 및 addStock() 메서드까지 실행이 완료되면OrderService의 cancelOrder메서드의 트랜잭션이 종료가 되어 flush가 발생/DB 쿼리가 날라감까지 제가 이해를 하고 있습니다.혹시 틀린 부분이 있다면 말씀부탁드리겠습니다!가장 헷갈리는 부분이 이건데,Order order.cancel() 이 부분에서order 라는 Order 엔티티 객체에 바로 cancel() 메서드를 사용했는데 /** * 주문 취소 */ public void cancel(){ if (delivery.getStatus() == DeliveryStatus.COMP) { throw new IllegalStateException("이미 배송완료된 상품은 취소가 불가능합니다."); } this.setStatus(OrderStatus.CANCEL); for (OrderItem orderItem : orderItems) { orderItem.cancel(); } }조회한 데이터는 Order 뿐인데 다음 코드가delivery.getStatus() 입니다.갑자기 delivery가 어디서 나온건지.. 모르겠습니다.예외처리가 끝나면 나오는 orderItem 또한 갑자기 무슨 데이터를 가지고 반복문을 돌리는지 모르겠습니다.제 짧은 지식의 추측상예외처리시에 delivery.getStatus() 는 Order엔티티와 1:1 관계이기에 delivery가 사용하게 될 때 지연로딩을 이용하여 delivery 데이터를 조회하여 사용하고OrderItem 또한 Order와 일대다관계이기에for (OrderItem orderItem : orderItems) { orderItem.cancel(); }이렇게 사용될 때 지연로딩을 이용하여Order pk를 가진 OrderItem 의 데이터를 가지고와 반복문을 돌리게 되는건가요??너무 헷갈리네요.. 제가 이해하고 있는게 맞나요?
-
해결됨웹소켓/STOMP 채팅서비스(spring, vue, redis)
섹션3. JWT토큰생성 부분에서 질문 있습니다.
안녕하십니까 선생님 !섹션3. JWT토큰생성 강의에서 작성하신 createToken 메서드를 보면 헤더를 따로 설정하지 않는데 JJWT 라이브러리가 SECRET_KEY를 참고하여 헤더를 설정해주는건지, 아니면 기본 설정 값이 들어가는 것인지 궁금합니다.createToken 메서드 :public String createToken(String email, String role){ Claims claims = Jwts.claims().setSubject(email); claims.put("role", role); Date now = new Date(); String token = Jwts.builder() .setClaims(claims) .setIssuedAt(now) .setExpiration(new Date(now.getTime()+expiration*60*1000L)) .signWith(SECRET_KEY) .compact(); return token; }
-
미해결스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
윈도우 gradlew build 실패
=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)예2. 인프런의 질문 게시판과 자주 하는 질문에 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예[질문 내용]여기에 질문 내용을 남겨주세요. 전부 해봤습니다. gradlew.bat 을 직접 폴더 내에서 눌러서 실행해보면 오류 코드고 뭐고 아무것도 확인할 수도 없이 바로 꺼져버립니다.jdk 24입니다. 그리고 커맨드를 열어서 cd 로 폴더 연결 후이대로 해보려고 해도 첫번째 사진처럼만 뜹니다. 다른 질문들을 다 찾아봐도 gradlew.bat 을 누르자마자 뭔가 보이기도 전에 꺼져버리는 케이스는 없어서 너무 고통스럽습니다. 도움을 주실 분 안 계실까요? https://drive.google.com/drive/folders/1r2uKWeNTWgDPhZMBnix_aDlhLFZClxPO?usp=sharing 파일 드라이브입니다
-
해결됨RabbitMQ를 이용한 비동기 아키텍처 한방에 해결하기
Receiver를 여러개 하려면 어떻게하나요?
@Bean public SimpleMessageListenerContainer container(ConnectionFactory connectionFactor, MessageListenerAdapter listenerAdapter) { SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(); container.setConnectionFactory(connectionFactor); container.setQueueNames(QUEUE_NAME); container.setMessageListener(listenerAdapter); return container; } @Bean public MessageListenerAdapter listenerAdapter(Receiver receiver) { return new MessageListenerAdapter(receiver, "receiveMessage"); // Receiver의 메소드명 }안녕하세요. 좋은 강의해주셔서 감사합니다.만약 하나의 큐에서 여러 컨슈머를 통해 동시에 여러개를 소비하고자한다면 Receiver를 추가해서 새롭게 MessageLisnterAdaper 빈을 추가하는걸까요? 어떤식으로 하는것인지 궁금합니다.여러큐와 거기에 따른 여러 컨슈머 구성시에는 어떤식으로 환경설정하면 될까요? 감사합니다.
-
해결됨1시간만에 치킨콤보값으로 배우는 서버 배포
윈도우 SSH접속 설정 도와주세요
https://jungle-case-018.notion.site/SSH-11a35c9c2899808aa2e3fefac15f5c87의 내용을 바탕으로 윈도우에서 SSH 접속이 가능하게 하려고 설정중인데, 확장자명이 없는 config파일을 만드는 법을 아무래도 모르겠습니다.(더보기 클릭)도 안되어서 도저히 못찾겠네요..도움 부탁드립니다.
-
미해결백엔드 개발자 성능 개선 초석 다지기
ngrinder 설치관련
안녕하세요 ngrinder 설치 할 때 window환경에서run_agent.bat 을 실행했는데 cmd창이 떴다고 바로 꺼지고 agentmanage에 아무것도 나오지 않습니다 제가 어떤 걸 놓쳤을까요??
-
해결됨스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판
Kafka Cluster 질문
Kafka Cluster 강의 약 11분 38초 쯤에서 Broker 2(topic 1, partition 2)에 장애가 발생하면, 다른 정상 Broker에서 Leader를 재선출 한다고 하셨는데, 그렇다면 해당 그림의 경우 Broker 1의 topic 1의 Leader가 partition 1에서 partition 2로 바뀌는 것인가요? 기존에 정상적으로 있던 Broker 1의 topic 1의 partition 1은 Leader의 자격을 잃는 것인가요?다시 말씀드리면, 정상 Broker에서 Leader를 재선출하면, 기존 토픽에 있는 Leader Partition이 어떻게 되는지 궁금합니다..!
-
해결됨스프링부트를 이용한 웹 프로그래밍: 웹사이트 이렇게 만드는 거예요!
AOP에 대한 설명
안녕하세요! Class08.스프링 프레임워크2 강의 중 8:00 쯤 "이 앞강에서 AOP 프로젝트가 있었죠...."라고 해주셨는데 해당 설명이 이전 강의 어디에서 해주셨는지 알 수 있을까요? AOP가 조금 추상적이어서요! 감사합니다.
-
미해결스프링 시큐리티 OAuth2
UserInfo 엔드포인트 요청 실습
scope 을 openId 하나만 설정하더라도 OidcUserService의 loadUser() 함수 안의 분기문에서 DefaultOAuth2UserService 를 계속 호출하길래 확인해보니 keycloak 에서 "scope": "openid profile email"형태로 리턴이 와서 항상 if 문에 걸리네요 keycloak 정책이 바뀐건지는 몰라도 내부적으로 저절로 추가하는 거 같습니다 버전도 강의와 동일한 19.0.1 버전입니다
-
해결됨스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판
HotArticle 관련 테스트 중에 질문드립니다.(verify, handle 관련)
안녕하세요. 강의 열심히 듣고 있습니다.Hot-article 관련 테스트 코드 중, verify 메소드에 대해 잘 몰라서 하는 질문일수도 있겠으나 헷갈리어 질문 드립니다.HotArticleServiceTest.java 코드를 작성중에@Test void handleEventIfScoreUpdatableEventTest() { // given Event event = mock(Event.class); given(event.getType()).willReturn(mock(EventType.class)); EventHandler eventHandler = mock(EventHandler.class); given(eventHandler.supports(event)).willReturn(true); given(eventHandlers.stream()).willReturn(Stream.of(eventHandler)); // when hotArticleService.handleEvent(event); // then verify(eventHandler, never()).handle(event); verify(hotArticleScoreUpdater).update(event, eventHandler); }위의 코드에서, verify(eventHanler, never()).handle(event); 로 검증하는 부분이 왜 never()가 되는걸까요?강의중에 eventHandler가 바로 불리지 않는다고 말씀하시긴 하셨는데, HotArticleService.java의 handleEvent() 메소드 안에서 hotArticleScoreUpdater.update(event, eventHandler) 를 호출하면 update()에서 전달한 eventHandler 의 handle()메소드를 결국에는 사용하게 되지 않나요?verify가 외부클래스인 HotArticleScoreUpdater의 메소드에서 사용하는거까지는 검지하지 못하기 때문일까요?
-
해결됨RabbitMQ를 이용한 비동기 아키텍처 한방에 해결하기
깃허브 step11 브랜치
Slack 관련된거 같은데, 강의에는 나와있지 않네요 ! step11은 혼자 공부하면 되는걸까요 ?
-
해결됨웹소켓/STOMP 채팅서비스(spring, vue, redis)
섹션6. 메시지 읽음처리, 채팅방 나가기, 11분 25초
안녕하세요 선생님,섹션6. 메시지 읽음처리, 채팅방 나가기, 11분 25초 쯤에 findByChatRoomAndMember(chatRoom, member); 코드로 모든 readStatus를 가져와서 읽음 처리를 하는 것으로 보입니다.읽지 않음 상태의 readStatus만 가져와서 읽음 상태로 바꿔주어도 상관이 없을까요 ?좋은 강의 정말 감사합니다.
-
해결됨스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판
OFFSET 기반 페이지네이션에서 인덱스를 활용한 성능 최적화 방식과 실제 테이블 접근 시점이 궁금합니다
안녕하세요 좋은 강의 감사드립니다~게시글 목록 조회시 select * from article where board_id = 1order by article_id desc limit 30 offset 1499970;1.여기 쿼리문에서 where 절이 먼저 실행되어 (board_id, article_id) 생성된 secondary Index 에서 board_id = 1 인 데이터들을 찾아서2.어차피 article_id 도 정렬이 되어 있으니 이 부분에서 offset 을 순차적으로 skip 하면서 결국에 마지막에3.select * 문을 수행하는게 아닌가요?2번 과정에서 order by article_id 를 위해 clusterd Index 에서 데이터를 조회하는 과정이 추가되나요? > 게시글 목록 API - 페이지 번호 기반 - N번 페이지, M개 게시글 - 설계 > 20:07마지막에 최종적으로 select * 문을 조회할때만 clusterd Index 에서 데이터를 조회하는거 아닌가요? 왜냐하면 이미 secondary Index 에서 article_id 가 정렬되어 있기 때문에 그냥 skip 하고 마지막에만 clusterd Index 에서 데이터를 조회할 것 같습니다.