묻고 답해요
161만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
해결됨실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
변경감지를 통한 데이터 변경에 대해서
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]안녕하세요. 김영한님 JPA강의를 모두 듣고 개인프로젝트를 하던 중에 궁금한 점이 생겨서 질문 올리게 되었습니다.김영한님께서 데이터의 업데이트는 변경감지를 통해서 하는 것이 좋다고 말씀하셨던 게 기억이 나는데요. 물론 변경감지만을 이용하라는 말씀은 아니시겠지만 변경감지를 통한 업데이트를 어디까지 사용해야 하는 가에 대해서 좀 의문이 생겼습니다.예를 들어 이런 경우입니다.회원이 게시판을 조회하면 조회수를 조회수를 증가시키는 로직인데요. (조회수 증가와 게시판 조회를 서로 다른 트랜잭션으로 했음)이럴 때에 변경감지를 통해서 조회수를 증가시킬 경우에 변경감지를 통해 변경된 값이 그대로 update되는 형태를 취하는 것인데, 이럴 경우 요청이 한 번에 많이 들오면 update순서가 보장되지 않아 정확한 조회수 증가가 이뤄지지 않을 것 같았습니다. 하지만 그렇다고 LOCK을 걸자니 조회수 하나 때문에 게시판 조회까지 LOCK이 걸려 조회 속도를 낮추는 것도 문제인 듯 하였습니다. 생각해 보면 이러한 조회수 증가는 쿼리를 UPDATE .. SET column = column +1 이런 식으로 만들고 처리하면 현재 컬럼의 값을 기준으로 1씩 증가하기 때문에 LOCK을 걸 필요도 없고 순서에 상관없이 일관된 값이 보장될 것 같은데 변경감지를 통해 값을 업데이트 하는 방식을 사용하면 현재 조회된 Entity의 조회수 값에 1을 추가하고 그 값으로 직접 UPDATE되는 형태라서 문제가 된 것이 아닐까 싶었습니다.이런 경우 UPDATE쿼리를 따로 날려 주는 방식이 더 효율적인 방식이 맞을까요? 실무에서는 어떤 방식을 취하고 있는 지 궁금합니다..!
-
미해결실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
@ModelAttribute 질문
@PostMapping("/members/new") public String create(@Valid @ModelAttribute MemberForm form, BindingResult result) { if (result.hasErrors()) { return "members/createMemberForm"; } Address address = new Address(form.getCity(), form.getStreet(), form.getZipcode()); Member member = new Member(); member.setName(form.getName()); member.setAddress(address); memberService.join(member); return "redirect:/"; }@GetMapping("/members/new") public String createForm(Model model) { return "members/createMemberForm"; }이런식으로 @ModelAttribute를 사용하여 코드를 짜보려고 하니 에러가 나왔고, 아마 html에서 memberForm 객체가 없어서 이용하지 못했고, 결국 매핑에 문제가 생겨 출력(?)을 하지 못해서 그런 것 같은데 예전 스프링 MVC강의에서는 @ModelAttriute를 이용해 코드를 잘 작성했던 거 같은데 왜 이번 강의에서는 사용하지 않았는지 궁금합니다. @PostMapping("/members/new") public String create(@Valid MemberForm form, BindingResult result) { MemberForm이 어떻게 파라미터로 받을 수 있는지 궁금합니다. (이미 Model에 객체가 있으면 파라미터로 받듯이 받을 수 있는 건지?) 위의 코드를 이용해서 웹페이지를 만드려면 createMemberForm.html 에서<form role="form" action="/members/new" th:object="${memberForm}" method="post">이부분 대신 html 파라미터를 넘겨주는 식으로 고쳐야하는 것 일까요? html을 잘 몰라 질문드립니다.
-
미해결스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
회원 리포지토리 테스크 케이스 findByName NoValue 에러 관련 질문입니다
package repository; import hello.hellospring.domain.Member; import hello.hellospring.repository.MemoryMemberRepository; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import java.util.List; import java.util.Optional; import static org.assertj.core.api.Assertions.*; class MemoryMemberRepositoryTest { MemoryMemberRepository repository = new MemoryMemberRepository(); @AfterEach public void afterEach() { repository.clearStore(); } @Test public void save() { //given Member member = new Member(); member.setName("spring"); //when repository.save(member); //then Member result = repository.findById(member.getId()).get(); assertThat(result).isEqualTo(member); } @Test public void findByName() { //given Member member1 = new Member(); member1.setName("spring1"); repository.save(member1); Member member2 = new Member(); member2.setName("spring2"); repository.save(member2); //when Member result = repository.findByName("spring1").get(); assertThat(result).isEqualTo(member1); //then } @Test public void findAll() { //given Member member1 = new Member(); member1.setName("spring1"); repository.save(member1); Member member2 = new Member(); member2.setName("spring2"); repository.save(member2); //when List<Member> result = repository.findAll(); //then assertThat(result.size()).isEqualTo(2); } } package hello.hellospring.repository; import hello.hellospring.domain.Member; import java.util.List; import java.util.Optional; public interface MemberRepository { Member save(Member member); Optional<Member> findById(Long id); Optional<Member> findByName(String name); List<Member> findAll(); }package hello.hellospring.repository; import hello.hellospring.domain.Member; import java.util.*; public class MemoryMemberRepository implements MemberRepository { private static Map<Long, Member> store = new HashMap<>(); private static long sequence = 0L; @Override public Member save(Member member) { member.setId(++sequence); store.put(member.getId(), member); return member; } @Override public Optional<Member> findById(Long id) { return Optional.ofNullable(store.get(id)); } @Override public List<Member> findAll() { return new ArrayList<>(store.values()); } @Override public Optional<Member> findByName(String name) { return store.values().stream() .filter(member -> member.getName().equals(name)) .findAny(); } public void clearStore() { store.clear(); } }이렇게 오류가 발생하는데 이유가 뭘까요? 아래와 같이 코드를 수정해보기도 하였습니다만, findAll 과 같이 노란색이 될뿐 초록색 마크가 나오지는 않습니다.@Test public void findByName() { // given Member member1 = new Member(); member1.setName("spring1"); repository.save(member1); Member member2 = new Member(); member2.setName("spring2"); repository.save(member2); // when Optional<Member> result = repository.findByName("spring1"); // then assertThat(result.isPresent()).isTrue(); assertThat(result.get()).isEqualTo(member1); }
-
미해결실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
OrderService에서는 find 메서드를 만들지 않은 이유가 따로 있나요 ?
ItemService에서는 ItemRepository의 기능을 위임받아 저장 및 검색 기능을 구현했는데OrderService에서는 검색기능을 구현하지 않았더라구요. ItemService에서 위임 받는 것 처럼 하면 간단할 것 같은데 굳이 만들지 않고 OrderRepository의 findOne기능을 사용하는 이유가 따로 있는 것일까요?
-
미해결실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
프로젝트 1:n 단방향 매핑 부탁드립니다.
https://drive.google.com/file/d/1tCpv16Aj0Q-U95EPxE43t-Y-8L1lidhy/view?usp=share_link Letter과 Member을 ManyToOne로 매핑하려고 했는데 데이터베이스를 보니 자꾸 null이 떠서 올립니다.. 파일은 에러가 생기기 전 잘 실행이 되는 파일입니다.
-
미해결토비의 스프링 부트 - 이해와 원리
imports 파일 생성
tobyspring.config.MyAutoConfiguration.imports 파일 생성 후 포맷이 변경되지 않는 것 같습니다.그냥 파일이 된 것 같습니다. imports 포맷의 파일로 생성이 되지 않는 것 같아서 따로 다운받거나 설정해야 할 것이 있을까요?
-
미해결스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술
JSP 회원 전체 조회가 안되네요.
질문 내용]여기에 질문 내용을 남겨주세요. 일단 두 사진 보시면, 예외 처리를 해주면 예외 처리가 try 내부 코드가 예외를 안던지니까 catch 빼라고 하고,예외 처리를 안해주면, IOException이 예외처리가 안됐다고 하네요..뭐가 문제죠..?람다를 사용했다고 해서 안될리가 없어보이는데 ㅠㅠ 추가로 out.write();가 Writer 추상 클래스의 write(); 메서드고 checked예외를 던지던데, try-catch로 안잡아줘도 되는 이유가 뭔가요??
-
해결됨스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
postman 응답 결과에 대한 질문입니다.
안녕하세요!선생님 강의를 재밌고 유익하게 수강하고 있는 학생입니다.스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 강의의 bean validation - HTTP 메시지 컨버터 부분을 수강하던 중 강의 내용과 제 결과 케이스의 다른 부분이 있어 다른 결과가 나오게 된 원인이 궁금하여 질문 드립니다!강의와 결과가 달랐던 부분은 postman으로 실패 요청을 send했을 때의 응답 결과 입니다.강의의 결과입니다.{ "timestamp": "2021-04-20T00:00:00.000+00:00", "status": 400, "error": "Bad Request", "message": "", "path": "/validation/api/items/add" }제가 실행한 결과입니다.{ "timestamp": "2021-04-20T00:00:00.000+00:00", "status": 400, "error": "Bad Request", "path": "/validation/api/items/add" }보이는 바와 같이, 제가 실행한 결과에서는 message에 대한 json 객체가 존재하지 않습니다. 혹시 스프링 부트의 버전과 관련된 부분에 있어서 차이가 있을까요?현재 사용 중인 스프링 부트 버전은 2.7.11 버전입니다.
-
미해결스프링 부트 웹 개발 입문 - 따라하며 배우기
logback-spring.xml 을 그대로 구현해봤는데요
저는 로그가 하나도 안찍히고 그냥 재생만 되네요.그리고 디버깅 버튼을 눌러도 저런식으로 로그가 아예 안나와요. 혹시 다른 추가적인 부분을 건드려야하나요?
-
해결됨토비의 스프링 부트 - 이해와 원리
@Import에서 @Component가 없어도 동작합니다.
강사님께서@Import 어노테이션은@Component 어노테이션이 붙은 혹은메타 어노테이션으로 @Component가 붙은 클래스를 컴포넌트 스캔 대상이 아닌 클래스를 구성정보로 등록할수 있다고 말씀해주셨습니다. 호기심에 Import할 클래스 레벨에 어노테이션을 붙이지 않고public class ServerConfigTest { @Bean Name1 name1(){ return new Name1(name3()); } @Bean Name2 name2(){ return new Name2(name3()); } @Bean Name3 name3(){ return new Name3(); } static class Name1{ Name3 name3; public Name1(Name3 name3) { System.out.println("네임 1 등장이요"); } } static class Name2{ public Name2(Name3 name3) { System.out.println("네임 2 등장이요"); } } static class Name3{ public Name3() { System.out.println("네임 3 등장이요"); } } }@MyAutoConfiguration @Import(ServerConfigTest.class) public class ServerTestBean2 { //코드.. }결과는 @Component,@Configuration(proxyBeanMethods=false)와 같은 결과가 로그로 보이더라구요 래퍼런스에는 일반 구성 클래스 as well as regular component classes(AnnotationConfigApplicationContext.register와 유사) 라고 적혀있는데 그 의미가import가applicationContext.register(ServerConfigTest) 같은 방식으로 동작한다고 생각하면 될까요??
-
해결됨스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
SessionConst
교재 6.로그인처리1 p 35 보면SessionConst가 있는데요 이거 enum으론 하면 안될까요?
-
미해결토비의 스프링 부트 - 이해와 원리
Service 계층에서 테스트 관련해서 질문이 있습니다.
안녕하세요 토비님제가 프로젝트를 진행하면서 도저히 모르는 부분이 있습니다.강의와 관련이 없는 질문이지만, 간절한 마음으로 질문해봅니다 Service 계층은 상태검증과 행위 검증에 대한 고민이 있습니다.특히, 객체의 책임과 테스트 범위에 대한 관점에 대한 차이때문에 고민이 있습니다.우선, 코드를 보여드리겠습니다.CommunityCommandService.updateCommunity는 커뮤니티의 소개란과 해시태그를 업데이트하는 부분입니다.//CommunityCommandService.java public void updateCommunity(Long userId, Long communityId, String description, List<String> newTags) { Community community = communityRepository.findCommunityById(communityId); memberQueryService.getManager(userId, communityId); community.updateCommunity(description, newTags); }해당 코드는 communityId로 community를 가져오고, userId / communityId로 요청한 유저가 메니저인지 확인합니다.그 후, community 객체에게 update를 위임합니다. 그러면 community 객체는 내부 상태값을 변경합니다. 여기서, 상태 검증인지 행위 검증인지에 따라 테스트가 달라집니다.public class CommunityCommandServiceTest { @Test void 상태검증_테스트() { Community community = new Community("dummy Intro", List.of("dummy tag")); given(communityRepository.findById(any)).willReturn(community); communityCommandService.updateCommunity(1L, "new intro", List.of("new tag")); assertThat(community.getIntroduce).isEqualTo("new intro"); assertThat(community.getTags).containsExactly("new tag"); } @Test void 행위검증_테스트() { Community community = mock(Community.class); given(communityRepository.findById(any)).willReturn(community); communityCommandService.updateCommunity(1L, "new intro", List.of("new tag")); then(community).should(times(1)).update("new intro", List.of("new tag")) } }상태검증_테스트의 검증 부분을 보면, 위임한 결과에 대해서 테스트를 진행하고 있습니다. Community 클래스의 update를 또 테스트하는 것 같은 느낌이 있습니다. 즉, 서비스 계층의 테스트 영역을 넘어서는 것인지 의문입니다.반면, 행위검증_테스트는 community.update가 호출하면서 위임했는지에 대해서만 테스트합니다. 하지만, 내부 로직에 하드코딩 되어있는 듯 합니다.사실 저는 상태검증을 더 선호합니다. 하지만 상태 검증이 객체지향스러운지 잘 모르겠습니다. 어느정도 감수해야하는 것 일까요? 정리하자면,상태검증CommunityCommandService.updateCommunity로 변경된 상태를 테스트개인적으로 선호하는 방식. 하지만, 상태를 테스트하기 때문에 객체지향의 관점에서 맞는지 확신이 없다. 테스트코드는 이 부분을 감수하는 것인지?행위검증협력한 객체의 행위에 대한 테스트객체지향의 관점에서 위임이 잘 이루어졌는지 테스트하는게 자연스럽다고 생각 질문하자면,서비스계층에서 Community.update()로 커뮤니티 내부 값에 대한 변경을 요청하였습니다.객체지향에서는 객체들이 서로 책임을 위임하며 상호작용하는 것이기 때문에Service 계층에서는 위임이 되었는지 호출 여부만 판단하는게 적절한지, 아니면, Service 계층에서 위임한 그 결과 Community의 내부값을 바꾼게 적절한 테스트인지..만약 이 방법이 맞다면 객체지향스럽다고 말할 수 있는지..?어떤 방식이 적절한지 잘 모르겠습니다. 이론적인 부분과 실제 테스트에 대한 괴리때문에 발생하는 문제 같습니다. 긴 질문 읽어주셔서 감사드립니다!
-
미해결실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
test/resources/application.yml이 없으면
main/resources/application.yml 을 따라가게 되는 건가요 ? 자바에서는 디비를 사용하고 싶고, test에서만 메모리 DB를 사용하고 싶다면 아무런 내용이 없는 application.yml를 test/resources 아래 생성만 하면 되는 건가요 ?
-
미해결스프링과 JPA 기반 웹 애플리케이션 개발
회원가입 인증 메일 확인
accountService 애서 processNewAccount 을 Account newAccount = saveNewAccount(signUpForm);;newAccount.getGenerateEmailCheckToken();accountRepository.flush();sendSignUpConfirmEmail(newAccount);로하면@Transactional 을 메서드 상단에 선언을 안해도 해결이 가능한데요. @Transactional 를 사용하지않고강제로 플러시를 해준경우에 추후에 문제가 될만 소지가 있을까요?
-
미해결스프링 DB 2편 - 데이터 접근 활용 기술
MyBatisItemRepository와 MybatisConfig
MyBatisItemRepostiory에서 @Repostiory와 @RequiredArgsConstructor을 통해서 스프링빈으로 등록하고 ItemMapper와 의존관계를 주입했잖아요근데 MYBatisConfig에서 다시MybatisConfig에 ItemMapper의존관계를 주입하는 이유가 뭔가요?
-
미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
서버에 파일업로드할 때는 어떻게하나요?
로컬에서 파일 업로드를 구현할 때는 강의때 해준 방법 처럼 file.dir변수에 C:/test_img/ 이런식으로 변수를 지정하고 저장을 하였는데 이번에 cafe24를 사용해서 프로젝트를 배포하는 작업중인데 어떻게 경로를 설정해야할 지 모르겠어서 질문드립니다ㅠㅠ cafe24에 문의 해보았는데 server.xml 파일에서 해당 경로를 매핑 해줘야 한다고 하였습니다.<Context docBase ="서버의 업로드 폴더 경로" path="localhost 뒤에 붙을 업로드 폴더의 경로" reloadable="true"/>https://yn971106.tistory.com/104?category=1016466https://kang0312.tistory.com/95https://tibang.tistory.com/entry/%EC%82%AC%EC%A7%84%EA%B0%99%EC%9D%80-%ED%8C%8C%EC%9D%BC-%EC%97%85%EB%A1%9C%EB%93%9C%EC%8B%9C-%EA%B2%BD%EB%A1%9C-%EC%84%A4%EC%A0%95%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95tomcat-%EA%B8%B0%EC%A4%80아래 블로그를 참고하라고 하긴하던데 매핑하는 방법만 알려주고, 파일업 업로드하는 로직은 없고, 구글링을 해봐도 강사님께서 알려주신 파일 업로드 로직과는 다른 글들 뿐이여서 파일 업로드 로직을 다 뜯어 고쳐야 하는건가.... 고민입니다.혹시 file.dir값만 딱 수정해서 서버에서 파일 업로드 할 수 있는 방법 아시면 알려주시면 감사하겠습니다.... 참고로 cafe24서버에 올린 폴더 경로입니다 upload폴더는 제가 생성한거고 권한도 777로 주었습니다.file.dir=/tomcat/webapps/upload/위와같이 경로 값을 주었는데 잘 작동하지 않습니다ㅠㅠ +추가강사님이 알려주신 file.dir을 통해 파일 경로를 지정하는 방법은 spring boot에서 톰캣서버를 내장하고 있어서 가능한건가요?? cafe24에 배포할 때는 서버를 내장하지 않으므로 server.xml에 <Context docBase="D:\IMAGES" path="/IMAGES" reloadable="false"/> 이거를 추가해야하는 건가요?? 서버쪽 영역으로 가니까 아직 모르는게 많네요ㅠㅠㅠ
-
미해결스프링 DB 1편 - 데이터 접근 핵심 원리
체크예외와 런타임예외의 차이점
수업과 직접적으로 관련있다고 보긴 어려운 질문이지만, 체크예외와 런타임예외에 대해서 궁금증이 생겨서 질문 남깁니다!아래 코드는 잘 실행됩니다. 그리고 IllegalStateException 오류가 잡히고 로그가 남습니다. @Test void b(){ try { ex2(); }catch (IllegalStateException e) { log.error("message",e); } } private void ex2() throws RuntimeException{ RuntimeException ex = new IllegalStateException(); throw ex; } 그러나 아래 코드는 오류를 제공합니다.@Test void a(){ try { ex1(); }catch (SQLNonTransientException e) { log.error("message",e); } } private void ex1() throws SQLException{ SQLException ex = new SQLNonTransientException(); throw ex; } ex1()에서 java.sql.SQLException 오류가 발생합니다.첫 번째 코드가 성공하고 IllegalStateException 오류가 잡히고 로그가 남으므로 두 번째 코드도 성공해야 한다고 생각합니다.SQLNonTransientException은 SQLException의 하위 예외이므로 IllegalStateException은 RuntimeException의 하위 클래스이기 때문입니다.런타임 에러일 때 에러가 발생하면 지정된 하위 에러로 잡을 수 있는데 Check예외인 SQLException의 경우에는 적용되지 않는 이유를 알고 싶습니다.
-
해결됨스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
세션 타임아웃 설정이 안됩니다.
안녕하세요.로그인 처리1 마지막 부분 세션 타임아웃 설정부분.pdf 41page가 안됩니다.application.properties 파일에서server.servlet.session.timeout=10이렇게 해보기도 하고SessionInfoController에서session.setMaxInactiveInterval(10);이렇게 해보아도 타임아웃이 되지 않습니다.제가 기대하는 것은 10초 뒤에는 세션아웃이 되어서 로그인이 필요한 페이지에 접근이 않되는 것인데요.제가 잘못이해하거나 설정이 안된 부분이 있다면 알려주시면 감사하겠습니다.제가 작성한 프로젝트는 여기에 올렸습니다.https://drive.google.com/file/d/1q1VbFHc21BpUgeTIjCvCd5WmXW-abwHm/view?usp=share_link
-
미해결스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술
JSP 로 보여지지 않아요
JSP 형식으로 보여지지 않아요
-
해결됨토비의 스프링 부트 - 이해와 원리
강사님 bind, bindOrCreate 차이 질문있습니다.
Binder.get(evn).bind("",bean.getClass());Binder.get(evn).bindOrCreate("",bean.getClass()); 해당 클래스 타입으로 Environment 빈 정보를 읽어서값을 바인딩해주고 null이 될경우 새 빈 오브젝트로 만들어준다고 하셨는데 1.여기서 바인딩 결과가 null이 될경우라는게 무슨 의 미인지 잘 이해가 안갑니다. 그리고 저희가 만든 @MyConfigurationProperties 어노테이션에는@Component가 메타어노테이션이 들어있고실제 스프링이 사용하는@ConfigurationProperties 에는@Component가 없더라구요@EnableConfigurationProperties에서는직접 레지스트리에 저장하는거같은데 제가 이해한게 맞을까요?