묻고 답해요
141만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
해결됨장애 없는 서비스를 만들기 위한 Resilience4j - CircuitBreaker
n 대의 서버간 서킷 브레이커의 상태를 동기화 시키려면 어떻게 해야 될까요?
가상의 상황외부 연동의 책임을 가진 external-interface 서버기상청에서 제공하는 api 로부터 오늘의 날씨 를 가져올 수 있습니다.n 대로 스케일 아웃 될 수 있습니다.현재 구성은 각 서버별로 독립적으로 서킷 브레이커를 관리하고 있습니다.ex)기상청 api 장애 상황에서1번 서버로 요청이 집중되어 1번 서버는 open 되었지만2번 서버는 요청이 별로 들어오지 않아 아직 close 상태일 수 있습니다.이 때 n 대의 서버간 서킷 브레이커의 상황을 필수적으로 공유해야 된다면 어떻게 할 것 인가? 생각하는 안redis pub/sub 기능을 통해 구현한다.1번 서버는 외부 연동 실패로 서킷브레이커가 open 되었다.1번 서버는 onStateTransition 의 로직으로 open 상태를 redis 에 pub 한다.2번 서버는 sub 하여 자신의 상태를 open 으로 동기화 한다.하지만 우려되는 맹점이 있습니다.pub, sub 방식은 서버간 동기화의 간극이 있습니다.결과적 일관성은 보장될지 언정 이 간극은 또 다른 이슈를 유발할 수 있습니다.ex)1번 서버는 open 상태를 pub 했습니다.2번 서버는 open 상태를 pub 했습니다. (1번 서버의 open 을 sub 하기 전에)1번 서버는 close 상태가 되었지만 (외부 연동이 정상적으로 복구되었음에도 불구하고)2번 서버에서 pub 한 open 정보에 의해 자신을 open 상태로 변경하였습니다. 동기화의 간극으로 인해 서로의 open 상태를 공유하는 것이 무한히 반복 됩니다.물론 실제 실패로의 event 만 pub/sub 하고,redis 의 상태 공유는 pub/sub 하지 않으면 될 것 같기도 합니다.하지만 가능/불가능 여부를 떠나 여전히 위험할 것 같다는 직감이 듭니다.redisson 과 같은 공유락까지도 생각을 해보았는데, 배보다 배꼽이 큰 것 같다는 생각이 듭니다.그외만약 actuator api 호출을 통해 명시적인 변경을 하고 싶다면실무라면 대표 도메인에 elb 나 ingress 등을 설정할 것 입니다.그러면 n 대의 서버에 상태 변경 명령을 어떻게하면 api 로 보낼 수 있나요?혹시 이건 아예 불가능한 생각일까요?사실 실무라면 굳이 서버간 서킷 브레이커의 상태를 공유하지 않을 것 같습니다.장애 상황이 자주 발생하지도 않을 것이거니와서킷 브레이커 발동을 위한 window 크기를 충분히 작게 설정한다면회복을 방해할 정도의 트래픽이 더 들어가지는 않을 것이라고 생각하기 때문 입니다.오히려 이런 공유 로직으로 인해 복잡도만 높아질 것이라고 생각해서 입니다. 하지만 실무를 떠나 기술적인 방법은 무엇이 있을까 인사이트가 궁금합니다.
-
해결됨스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
build 가 되지 않습니다.
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오) 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 아니오3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) 예[질문 내용]여기에 질문 내용을 남겨주세요. 빌드하려는 과정에서 해당 폴더에 접근할 수 없다고 뜹니다.어떻게 하면 접근할 수 있는지 궁금합니다.
-
해결됨스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
자바 버전이 다르게 나옵니다
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오) 아니오2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) 예[질문 내용]여기에 질문 내용을 남겨주세요. 현재 윈도우를 사용하고 있습니다. jdk21 을 intellij 내에서 설치하고, 커뮤니티에 있는 질문을 살펴보며 환경변수를 설정하고, 빌드하려고 했습니다. 그런데 전 분명 환경 변수에 jdk21을 설정했는데 cmd 창에 java -version 을 입력하면 openjdk11u ~~ 가 나옵니다. 예전에 다운 받았던 것이 잘못 나왔나 싶어 모두 찾아 삭제해봤는데 여전히 있습니다. intellij 에서 확인해봤을 때도 build.gradle 은 자바 버전이 21이고, 그에 따라 jdk 버전 21을 설정해놨었습니다. 어떻게 해야 자바 버전이 21로 나올까요?환경변수로 지정해놓은 JAVA_HOME을 눌러 해봤는데도 자바 버전이 21로 나오지 않습니다.
-
미해결호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
dto 변환
회원가입과 비밀번호 암호화 - 1에 5분 10초 정도 쯤에 dto를 서비스 계층으로 바로 넘기는 것이 안 좋다고 호돌님께서 말씀하셨습니다. 그래서 궁금증을 해결해 보고자 검색을 해봤습니다. 근데 dto를 컨트롤러 계층에서 엔티티로 변환하고 서비스 계층으로 넘겨야한다는 의견도 있고 컨트롤러에서 변환하고 넘기는 것보다 서비스 계층에서 dto를 변환해야 한다는 의견이 있는데 호돌님께서 말씀하신 것이 제가 고민하고 있는 것이 맞는지 알고 싶고 호돌님께서는 왜 dto를 서비스 단에 바로 넘기는 것이 안 좋다고 했는지 궁금합니다.!! 의견이 너무 갈려서 뭐가 맞는 방법인지 헷갈리네요
-
해결됨실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
Book 정적 생성 메서드 사용 시, 수정 X
Book 객체 인스턴스를 생성할 때, Setter로 값을 세팅하는 것이 아니라, 이전에 배운 '정적 생성 메서스' 방식을 활용해보았습니다. 그런데, 이 방식으로 진행할 경우에는 수정 시, item.getId() ==null 이 ture가 되어 em.persist를 해버립니다.(즉, 수정을 위한 merge가 동작하지 않습니다.)[Book.class] [ItemController.class] ※이후의 Repository 관련한 코드는 모두 강의 내용과 동일합니다!!그래서 ItemRepository의 if 구문에 break point를 찍고 디버깅을 해보았는데,상품 등록 시, Item의 Id값이 세팅이 안되었더라고요. DB 테이블을 확인해보니 Item_id 값은 잘 들어가 있는데, 왜 아래와 같이 수정 시에도 id = null 인지 짧은 지식으로 이해가 안갑니다..!
-
해결됨스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
save 메서드 리턴값
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]이 save메서드를여기서 repository.save(member); 로 사용할때save메서드 내에서 리턴되는 member는 사용되지 않고버려지는건가요? 아님 어떻게 되는건가요?
-
해결됨실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
테이블을 만드는 테스트가 계속 실패합니다.
Description:Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.Reason: Failed to determine a suitable driver classAction:Consider the following:If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.If you have database settings to be loaded from a particular profile you may need to activate it (no profiles are currently active).이러한 오류만 반복이 되는데요 데이터 베이스 설정화면과application.yml 입니다 - Spring과 jpa가 동일한 선상에 있으면 안된다는 글도 읽게되어서 고쳤지만 같은 오류가 발생하였습니다...spring: datasource: url: jdbc:h2:tcp://localhost/~/jpashop username: sa password: driver-class-name: org.h2.Driver jpa: hibernate: ddl-auto: create properties: hibernate: # show_sql: true format_sql: true logging.level: org.hibernate.SQL: debug org.hibernate.orm.jdbc.bind: trace맨처음에는 포트번호가 같아서 접속이 불가능 하다고하여 포트번호도 바꾸고 이것저것을 다 해봤는데 안돼어서 문의 남깁니다..ㅜㅜ 도와주세요ㅜㅜ
-
미해결스프링 시큐리티
ajax 구현 부분 작동이 안되서 질문드립니다.
ajax가 아예 진행이 안되서 질문드립니다. springSecurity6, 스프링부트 3.2.1 사용중입니다. 코드는 아래와 같은데 이게 어디서 어디가 틀렸는지를 도저히 모르겠습니다.ajax 전까지 form 방식은 정상적으로 작동하고 있으며,ajax 요청 보낼 시 POST http://localhost:8080/api/loginorg.apache.http.client.ClientProtocolException 이런 에러가 발생합니다. ajax.http 파일은 강의 문서를 다시 다운받아 했으며 postman으로 요청시 이유는 모르겠지만 get 요청으로 처리되고 위의 요청시 아래와 같은 로그 발생합니다 10000자 제한떄문에 댓글로 변경 curl 요청시 아래와 같습니다 10000자 제한떄문에 댓글로 변경 아래 코드에서 csrf disable을 하였음에도 계속 동일한 상태이고 강의 git 코드를 여러 브랜치에서 계속 참고해서 막 섞여있어서 어디서부터 고쳐야될지 전혀 모르겠습니다. 거의 6시간 넘게 헤매고 있는데 전혀 모르겠습니다. 혹시 확인 가능하시면 변경해야될 부분 부탁드립니다. 감사합니다. @Configuration @Slf4j @Order(0) public class AjaxSecurityConfig { @Autowired protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(authenticationProvider); } @Qualifier("ajaxAuthenticationProvider") @Autowired private AuthenticationProvider authenticationProvider; // 변경된 부분 @Bean protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception { log.info("여기옴2"); http .authorizeHttpRequests(auth-> auth .requestMatchers("/api/login").permitAll() .anyRequest().authenticated() ); http.exceptionHandling(exceptionHandling -> exceptionHandling .authenticationEntryPoint(new AjaxLoginAuthenticationEntryPoint()) .accessDeniedHandler(ajaxAccessDeniedHandler()) ); http.addFilterBefore(ajaxLoginProcessingFilter(), UsernamePasswordAuthenticationFilter.class); http.csrf(csrf -> csrf.disable()); return http.build(); } @Autowired private ObjectMapper objectMapper; @Autowired private AuthenticationConfiguration authenticationConfiguration; @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception { return authenticationConfiguration.getAuthenticationManager(); } @Bean public AuthenticationSuccessHandler ajaxAuthenticationSuccessHandler(){ return new AjaxAuthenticationSuccessHandler(); } @Bean public AuthenticationFailureHandler ajaxAuthenticationFailureHandler(){ return new AjaxAuthenticationFailureHandler(); } public AccessDeniedHandler ajaxAccessDeniedHandler() { return new AjaxAccessDeniedHandler(); } @Bean public AjaxLoginProcessingFilter ajaxLoginProcessingFilter() throws Exception { AjaxLoginProcessingFilter filter = new AjaxLoginProcessingFilter(); filter.setAuthenticationManager(authenticationManager(authenticationConfiguration)); filter.setAuthenticationSuccessHandler(ajaxAuthenticationSuccessHandler()); filter.setAuthenticationFailureHandler(ajaxAuthenticationFailureHandler()); return filter; } // @Bean // public AjaxLoginProcessingFilter ajaxLoginProcessingFilter(){ // AjaxLoginProcessingFilter ajaxLoginProcessingFilter = new AjaxLoginProcessingFilter(); // ajaxLoginProcessingFilter.setAuthenticationManager(authenticationManagerBean()); // return ajaxLoginProcessingFilter; // } } @Slf4j @Component public class AjaxAuthenticationProvider implements AuthenticationProvider { @Autowired private UserDetailsService userDetailsService; @Autowired private PasswordEncoder passwordEncoder; /** * 검증에 대한 구현 부분 * * 여기서 전달받는 authentication 객체는 AuthenticationManager에서 전달받는 것 * @param authentication the authentication request object. * @return * @throws AuthenticationException */ @Transactional @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { String username = authentication.getName(); String password = (String) authentication.getCredentials(); log.info("ajax Authentication"+ authentication); log.info("ajax username : "+username); log.info("ajax password : "+password); /** * db에 있는 사용자를 가져오는 부분 */ AccountContext accountContext = (AccountContext)userDetailsService.loadUserByUsername(username); if (!passwordEncoder.matches(password, accountContext.getPassword())){ throw new BadCredentialsException("ajax invalid password!"); } /** * 섹션 4-8에서 secret key 추가해서 검증하는 부분 */ // FormWebAuthenticationDetails formWebAuthenticationDetails = (FormWebAuthenticationDetails) authentication.getDetails(); // String secretKey = formWebAuthenticationDetails.getSecretKey(); // // if (secretKey == null || !"secret123".equals(secretKey)){ // throw new InsufficientAuthenticationException("secret key invalid"); // } AjaxAuthenticationToken authenticationToken = new AjaxAuthenticationToken(accountContext.getAccount(),null,accountContext.getAuthorities()); // log.info(""+accountContext.getAccount()); // log.info(""+authenticationToken); return authenticationToken; } /** * parameter 로 전달되는 authentication 타입과 여기서 검증하려는 대상의 타입이 일치하면 검증하도록 하는거 * @param authentication * @return */ @Override public boolean supports(Class<?> authentication) { return authentication.equals(AjaxAuthenticationToken.class); } } public class AjaxAuthenticationToken extends AbstractAuthenticationToken { private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; private final Object principal; private Object credentials; /** * 인증받기전에 사용자가 입력한 아이디와 비밀번호 담는 생성자 * */ public AjaxAuthenticationToken(Object principal, Object credentials) { super(null); this.principal = principal; this.credentials = credentials; setAuthenticated(false); } /** * 여기는 인증 완료후 아이디와 비번, 권한정보를 담는 생성자 */ public AjaxAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) { super(authorities); this.principal = principal; this.credentials = credentials; super.setAuthenticated(true); // must use super, as we override } public static UsernamePasswordAuthenticationToken unauthenticated(Object principal, Object credentials) { return new UsernamePasswordAuthenticationToken(principal, credentials); } public static UsernamePasswordAuthenticationToken authenticated(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) { return new UsernamePasswordAuthenticationToken(principal, credentials, authorities); } @Override public Object getCredentials() { return this.credentials; } @Override public Object getPrincipal() { return this.principal; } @Override public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException { Assert.isTrue(!isAuthenticated, "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead"); super.setAuthenticated(false); } @Override public void eraseCredentials() { super.eraseCredentials(); this.credentials = null; } } @Slf4j public class AjaxLoginProcessingFilter extends AbstractAuthenticationProcessingFilter { private ObjectMapper objectMapper = new ObjectMapper(); private static final String XML_HTTP_REQUEST = "XMLHttpRequest"; private static final String X_REQUESTED_WITH = "X-Requested-With"; public AjaxLoginProcessingFilter() { /** * 여기서 정한 경로의 요청만 받음 * 여기서 path 가 일치해야만 수행함 */ super(new AntPathRequestMatcher("/login", "POST")); } @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException { log.info("isAjax processing"); if (isAjax(request)){ throw new IllegalStateException("Authentication is not supported"); } AccountDto accountDto = objectMapper.readValue(request.getReader(), AccountDto.class); /** * accountDto 비어있는지 확인하는 부분인데 StringUtils.isEmpty 가 deprecated 되어서 ObjectUtils.isEmpty 사용 */ if (ObjectUtils.isEmpty(accountDto.getUsername()) || ObjectUtils.isEmpty(accountDto.getPassword())){ throw new AuthenticationServiceException("Username op Password is empty"); } AjaxAuthenticationToken token = new AjaxAuthenticationToken(accountDto.getUsername(), accountDto.getPassword()); return this.getAuthenticationManager().authenticate(token); } /** * 클라이언트와 약속 정해서 * 그게 참이면 ajax 요청이 맞다고 판단함 * @param request * @return */ private boolean isAjax(HttpServletRequest request) { return "XMLHttpRequest".equals(request.getHeader("X-Requested-With")); } }
-
미해결스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
h2.Driver 오류와 HelloProjectApplication 실행오류
[질문 내용]여기서 org.h2.Driver 인식이 안되는 문제가 발생합니다. 초록색으로 떠야하는 것 같은데 왜 인식이 안되는지 모르겠어요. build.gradle에도 dependencies에도 h2관련되어서 잘 추가했고 뒤에 띄어쓰기도 되어 있지 않습니다! 이 상태에서 HelloProjectApplication을 실행시켰더니 여러 오류들이 뜨는 것을 발견했습니다. com.zaxxer.hikari.pool.HikariPool j.LocalContainerEntityManagerFactoryBeano.s.boot.SpringApplication 이 3곳에서 에러가 발생합니다. 아래에 소스코드 파일 구글드라이브 첨부드립니다.. 감사합니다!https://drive.google.com/drive/folders/18tc10v1OtuO7iS6ZFTqcy0ag6eJH0NGU?usp=sharing
-
해결됨실전! 스프링 데이터 JPA
커스텀 인터페이스 구현체 이름 작성 시 순환 참조 발생 질문
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오) 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) 예[질문 내용]여기에 질문 내용을 남겨주세요. 안녕하세요 영한님. 이번 강의도 잘 수강한 학생입니다.강의 내용과 공식 문서를 참고하며 간단한 게시글 리포지토리를 만들어보고자, 아래처럼 작성했었습니다. public interface BoardRepository { Long save(final Board board); ... }@Repository @RequiredArgsConstructor public class BoardJpaRepositoryImpl implements BoardRepository { private final BoardJpaRepository boardJpaRepository; ... }public interface BoardJpaRepository extends JpaRepository<Board, Long> { }강의 내용대로 스프링 데이터 JPA인 BoardJpaRepository에 Impl을 붙인 BoardJpaRepositoryImpl을 만들었는데, 아래와 같이 순환 참조 문제가 발생했습니다. The dependencies of some of the beans in the application context form a cycle: boardController defined in file [/Users/hyunjoonchoi/Desktop/2024/2024-mju-mentoring/build/classes/java/main/com/mju/mentoring/board/controller/BoardController.class] ↓ boardService defined in file [/Users/hyunjoonchoi/Desktop/2024/2024-mju-mentoring/build/classes/java/main/com/mju/mentoring/board/service/BoardService.class] ┌─────┐ | boardJpaRepositoryImpl defined in file [/..../build/classes/java/main/com/mju/mentoring/board/infrastructure/BoardJpaRepositoryImpl.class] └─────┘ Action: Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true. 그런데 이름을 BoardRepositoryImpl, JpaBoardRepositoryImpl 등 다른 이름으로 하니까 순환 참조 문제가 해결되더라구요이에 대해서 다른 외국 개발자 분들도 이슈로 올리고, 그것들을 찾아봤으나 메인테이너 분이 @Lazy를 사용하거나 ObjectProvider를 사용해야 한다고 말씀해주시는 것 밖에 없었습니다. 공식 문서 (https://docs.spring.io/spring-data/jpa/docs/1.5.0.RELEASE/reference/html/repositories.html#repositories.create-instances)를 보면 스프링 데이터 JPA + Impl로 할 시 자동으로 스프링 데이터 JPA의 구현체로 인식한다고 되어 있는 것 같은데, 이런 순환 참조 이슈가 발생한 원인을 알 수 있을까요? 이를 해결해보고자 스프링 데이터 JPA 레포지토리에 직접 이슈로 남기고 (https://github.com/spring-projects/spring-data-jpa/issues/3320) 답변을 받았긴 했습니다만, 여전히 다른 이야기를 하시는 것 같아 영한 님께도 문의드리고 싶습니다!
-
해결됨스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
h2 데이터베이스 연결 문제
[질문 내용]db 생성 확인하고 주신대로 입력하였는데 이런 오류가..ㅠ 도와주심 감사하겠습니다.
-
미해결실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
정적 생성 메서드 파라미터에는 DTO or Parameters ?
package jpabook.jpashop.domain.item; import jakarta.persistence.DiscriminatorValue; import jakarta.persistence.Entity; import jpabook.jpashop.controller.BookForm; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import static lombok.AccessLevel.PROTECTED; @Entity @DiscriminatorValue("B") @Getter @Setter @NoArgsConstructor(access = PROTECTED) public class Book extends Item { private String author; private String isbn; // public static Book createBook(String author, String isbn, String name, int price, int stockQuantity) { // Book book = new Book(); // book.setName(name); // book.setPrice(price); // book.setStockQuantity(stockQuantity); // book.setAuthor(author); // book.setIsbn(isbn); // // return book; // } public static Book createBook(BookForm form) { Book book = new Book(); book.setName(form.getName()); book.setPrice(form.getPrice()); book.setStockQuantity(form.getStockQuantity()); book.setAuthor(form.getAuthor()); book.setIsbn(form.getIsbn()); return book; } }이렇게 정적 생성 메서드의 파라미터를 Dto형태로 두는 것과 파라미터를 전부 명시적으로 적는 것 중에서 어떤 코드가 더 나은 코드일까요?createBook을 호출해서 사용할 때도, 인자 값을 Dto 하나만 넣어주면 되고, 실제 스펙은 BookForm에서 간단하게 확인 가능하다고 판단했습니다..!
-
해결됨실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
Entity, dto관련 질문
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)[질문 내용]강의를 들으며 정리하는 중에 모르는 점이 생겨 질문을 남기게 되었습니다.필드변수가 똑같음에도 book클래스가 아닌 bookForm클래스로 매개변수를 가진다-> view 레이어와 db 레이어 사이의 역할을 분리하기 위해서bookForm이라는 dto를 매개변수로 사용하고entity는 사용하지 않는다. entity 객체에서 @setter를 사용한다.-> builder패턴을 사용할 수 있지만 예제라서 단순화 했고 실제 코드를 짤 때는 setter를 지양하는 것이 좋다(직접적으로 변경될 수 있기 때문에) setter가 꼭 필요한 상황에서는 사용해도 된다.-> 솔직히 어떤 상황에서 인지 잘 이해가 가지 않습니다.데이터 값을 하나하나 바꾸는 게 흔한 상황..? 이라던지 예시를 들어주실 수 있을까요? 각 제가 생각한 답변 보충해주고 싶으신 거나 틀린 부분이 있다면 수정 부탁드립니다!
-
해결됨실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
연관관계 편의 메서드 작성 방법에 대해 제가 이해한 것이 맞는지 피드백 부탁드립니다.
<Member 클래스>package jpabook.jpashop.domain; import jakarta.persistence.*; import lombok.Getter; import lombok.Setter; import java.util.ArrayList; import java.util.List; @Entity @Getter @Setter public class Member { @Id @GeneratedValue @Column(name = "MEMBER_ID") private Long id; private String name; @Embedded private Address address; @OneToMany(mappedBy = "member") // 읽기 전용 private List<Order> orders = new ArrayList<>(); }<Order 클래스>package jpabook.jpashop.domain; import jakarta.persistence.*; import lombok.Getter; import lombok.Setter; import java.time.LocalDateTime; import java.util.List; import static jakarta.persistence.FetchType.*; @Entity @Table(name = "ORDERS") @Getter @Setter public class Order { @Id @GeneratedValue @Column(name = "ORDER_ID") private Long id; @ManyToOne(fetch = LAZY) @JoinColumn(name = "MEBMER_ID") private Member member; @OneToMany(mappedBy = "order", cascade = CascadeType.ALL) private List<OrderItem> orderItems; @OneToOne(fetch = LAZY ,cascade = CascadeType.ALL) @JoinColumn(name = "DELIVERY_ID", unique = true) private Delivery delivery; private LocalDateTime orderDate; // 주문시간 @Enumerated(EnumType.STRING) private OrderStatus status; //todo: 연관관계 메서드(양방향) public void setMember(Member member) { this.member = member; // Order -> member member.getOrders().add(this); // member -> order } }Order쪽에서 멤버와의 관계는 @ManyToOne이라 수정, 삭제가 가능해서 연관관계 메서드를 작성할 필요가 없지만, Member 쪽은 mappedBy로 매핑되어있기 때문에 Order를 참조할 때 읽기만 가능하다고 들었습니다. 따라서 Member쪽에서도 Order를 수정, 삭제를 하려면 값을 설정해야 하기 때문에 연관관계 메서드를 작성해야만 양방향 참조가 가능하다고 이해하였습니다.정리하자면, 다 관계는 연관관계 메서드를 작성할 필요가 없고, 일 관계에서만 연관관계 메서드를 작성해야 양방향 참조가 가능한 것이 맞나요?
-
미해결실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
연관관계 편의 메서드 작성 방법에 대해서 제가 이해한 것이 맞는지 피드백 부탁드립니다.
package jpabook.jpashop.domain; import jakarta.persistence.*; import lombok.Getter; import lombok.Setter; import java.util.ArrayList; import java.util.List; @Entity @Getter @Setter public class Member { @Id @GeneratedValue @Column(name = "MEMBER_ID") private Long id; private String name; @Embedded private Address address; @OneToMany(mappedBy = "member") // 읽기 전용 private List<Order> orders = new ArrayList<>(); }
-
미해결자바와 스프링 부트로 생애 최초 서버 만들기, 누구나 쉽게 개발부터 배포까지! [서버 개발 올인원 패키지]
31강 질문 있습니다.
책 대출기능 DB 테이블명을 'user_loan_hostory'라고 오타를 내서 생성을 하니까 책 대출 시 서버 내부 오류가 발생했습니다.그래서 테이블명을 'user_loan_history'로 수정한 후 대출 기능을 다시 실행해보니 잘 동작합니다. 왜 테이블명이 user_loan_hostory일 때는 인식하지 못하고, user_loan_history로 수정한 후에만 인식이 되는걸까요? db 테이블과 엔티티 객체를 매핑할 때, 대응되는 필드만 맞으면 DB 테이블명을 명시적으로 작성해주지 않아도 스프링이 알아서 인식하는 것이 아닌가요?ㅜㅜ
-
미해결실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
강의 마지막, 생성된 SQL문을 그대로 쓰면 안된다고 하신 말의 의미
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/)[질문 내용]강의 마지막에 JPA가 생성해준 SQL문들이 콘솔에 쫙 찍히잖아요. 영한님께서 이걸 그대로 복사해서 쓰면 안되고, 고치고 수정해서 사용해야 한다고 하셨는데요. JPA가 이미 자동으로 DB에 테이블을 생성해준 것 아닌가요? 왜 또 create 쿼리를 쓸 일이 있다는듯이 말씀하시는건지 이해가 안갔습니다.JPA 기본편을 점프하고 바로 넘어와서 이해가 안가는건진 모르겠지만, JPA기술 자체가 자동으로 DB를 생성하고 관리해주는 것에 의의가 있는 것 아닌가요? 쿼리문 생성용으로 쓰는거라면 그냥 시중에 erd 그리는 GUI 툴들이 많은데 그걸 쓰면 되는 것이 아닌지.. 질문드립니다.
-
미해결스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
1강 프로젝트 생성 강의 중
run 을 눌러서 0808을 확인해야 하는데 run자체가 안되네요. 왜 이러는 걸까요!?
-
미해결스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
앞으로 사야할 유료 강의들 소스코드 제공 여부가 궁금합니다
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오) 네2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 네3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) 아니요[질문 내용]여기에 질문 내용을 남겨주세요.무료강의 잘들었습니다, 감사합니다,이제 스프링과 스프링부트,JPA 완전정복 로드맵 구매를 고려하고 있는데앞선 다른 질문글보니 이 강의는 소스코드가 따로 github에 공유가 안되는데저 로드맵들에 있는 유료강의에서는 제공이 되나요?ps. 제가 공부할때 필기하며 싹 강의 듣고 코드보면서 공부하는 스타일라서요
-
미해결스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
회원 조회가 되지 않습니다 ㅠ.ㅠ..
[질문 내용]오타가 있는지 꼼꼼히 확인했는데도 조회하지 못해 글 올립니다. 회원을 입력하고 조회할 때의 페이지 소스를 보면 값은 제대로 들어갔는데 뷰로 보이지가 않습니다. https://drive.google.com/file/d/10VLD9t8EssyDyLIPXITCINxJUngSrQyz/view?usp=sharing