월 17,380원
5개월 할부 시다른 수강생들이 자주 물어보는 질문이 궁금하신가요?
- 미해결호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
실습 예제 소스 공유 신청합니다.
안녕하세요.참고용 소스 git 신청하라는 공지 봤을때는 결제만 하구 아직 수업듣기 전이라..들을때 신청해야지 했었는데요.지금 들을려구 보니 새소식에서 목록이 사라져 있어서요.혹시 git 읽기 권한 가능하다면 공유 부탁드립니다.인프런 id : startek7@daum.netgithub 로그인 메일 : startek7@daum.net감사합니다.
- 미해결호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
인텔리J 플러그인이 궁금합니다
강의 중간 중간에 호돌맨님께서 빌드 후 테스트 하고나면 캡쳐화면처럼 방금 전에 수행한 후 line by line 으로 각 변수의 사이즈, 값, 타입들이 나오더라고요.신기한 플러그인 같은데 혹시 어떤 플러그인일까요??감사합니다.
- 미해결호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
postService 목록 조회 단위 테스트
당연히 구글링 해보셨져? 원하는 결과를 못찾으셨나요? 어떤 검색어를 입력했는지 알려주세문제가 발생한 코드(프로젝트)를 Github에 올리시고 링크를 알려주세요. 안녕하세요. 호돌맨님.postService에서 목록 조회하는 단위테스트에 대해서 질문이 있습니다. 먼저 목록 조회 코드부터 보여드리면postService.findPosts 는 postQueryRepository에 구현체로 만들었습니다.postService@Slf4j @Service @RequiredArgsConstructor @Transactional(readOnly = true) public class PostService { public static final String ENTITY_NAME = "post"; private final PostQueryRepository postQueryRepository; private final PostRepository postRepository; /** * Post 목록 조회 */ public Page<Post> findPosts(PostSearchCondition condition, Pageable pageable) { return postQueryRepository.findPosts(condition, pageable); } }postQueryRepository@Repository public class PostQueryRepository { private final JPAQueryFactory queryFactory; public PostQueryRepository(EntityManager em) { this.queryFactory = new JPAQueryFactory(em); } /** * Post 목록 조회 */ public Page<Post> findPosts(PostSearchCondition condition, Pageable pageable) { List<Post> content = getPostList(condition, pageable); JPAQuery<Long> count = getPostListCount(condition); return PageableExecutionUtils.getPage(content, pageable, count::fetchOne); } /** * Post 목록 */ private List<Post> getPostList(PostSearchCondition condition, Pageable pageable) { return queryFactory .select(post) .from(post) .where( searchCondition(condition.getSearchCondition(), condition.getSearchKeyword()) ) .offset(pageable.getOffset()) .limit(pageable.getPageSize()) .orderBy(post.id.desc()) .fetch(); } /** * Post 목록 카운트 */ private JPAQuery<Long> getPostListCount(PostSearchCondition condition) { return queryFactory .select(post.count()) .from(post) .where( searchCondition(condition.getSearchCondition(), condition.getSearchKeyword()) ); } /** * where searchCondition LIKE '%searchKeyword%' */ private BooleanExpression searchCondition(SearchCondition searchCondition, String searchKeyword) { if (searchCondition == null || !hasText(searchKeyword)) { return null; } if (SearchCondition.TITLE.equals(searchCondition)) { return post.title.contains(searchKeyword); } else if (SearchCondition.CONTENT.equals(searchCondition)) { return post.content.contains(searchKeyword); } else { return null; } } } 다음은 테스트 코드입니다.postServiceTest@ExtendWith(MockitoExtension.class) class PostServiceTest { //CREATE_POST public static final String POST_TITLE = "post_title"; public static final String POST_CONTENT = "post_content"; //UPDATE_POST public static final String UPDATE_TITLE = "update_title"; public static final String UPDATE_CONTENT = "update_content"; //ERROR_MESSAGE public static final String ENTITY_NAME = "post"; public static final Long NOT_FOUND_ID = 1L; public static final String HAS_MESSAGE_STARTING_WITH = "존재하지 않는 "; public static final String HAS_MESSAGE_ENDING_WITH = "id = "; @InjectMocks PostService postService; @Mock PostQueryRepository postQueryRepository; @Mock PostRepository postRepository; private Post getPost(String title, String content) { return Post.createPostBuilder() .title(title) .content(content) .build(); } @Test @DisplayName("post 목록 조회") void findPosts() { //given List<Post> posts = new ArrayList<>(); for (int i = 0; i < 30; i++) { posts.add(getPost(POST_TITLE + i, POST_CONTENT)); } //검색 안먹힘 PostSearchCondition condition = new PostSearchCondition(); condition.setSearchCondition(SearchCondition.TITLE); condition.setSearchKeyword("0"); PageRequest pageRequest = PageRequest.of(0, 10); given(postQueryRepository.findPosts(condition, pageRequest)).willReturn(new PageImpl<>(posts)); //when Page<Post> contents = postService.findPosts(condition, pageRequest); //then assertThat(contents.getTotalElements()).isEqualTo(3); assertThat(contents.getContent().size()).isEqualTo(3); } } 여기서 findPosts 단위 테스트를 진행하려고 하는데요.목록을 30개를 만들고 검색조건과 페이지 정보를 파라미터로 넘겨서 검색이 된 결과가 나올것이라고 예상했지만 검색조건은 먹히지 않고 30개의 목록만 리턴이 됩니다. 전 검색 조건대로 "0"들어간 title만 검색이되어서contents.getTotalElements() == 3, contents.getcontent().size() == 3 으로 예상을 했는데contents.getTotalElements() == 30, contents.getcontent().size() == 30 로 결과가 나와 테스트에 실패하게됩니다. 구글링으로 mockito, page, test, querydsl, parameter 등 다양하게 검색 해봤는데 원하는 결과를 얻기 못해 질문 한번 드려봅니다. 혹시나 제가 잘못된 방향으로 테스트를 진행하고 있는건지 혹은 다른 방법이 있을지 궁금합니다!깃저장소도 같이 공유드립니다.https://github.com/heechul90/heech-heechlog-server
- 미해결호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
안녕하세요. 레포지토리 구성원 참여 부탁드립니다.
안녕하세요. 늦었지만 프로젝트 레포지토리 구성원 참여부탁드리겠습니다.강의 잘보고 있습니다. 감사합니다:)메일 : gyuseok6394@gmail.com
- 해결됨호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
Spring Boot 버전 2.5.9가 없을 경우
Spring Boot 2.5.9를 선택할 수 없으면2.7.X or 2.6.X로 진행해도 상관없나요?
- 미해결호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
소스코드
저번에 소스코드 이메일적는시기를 놓쳤는데 지금이라도 가능할까요!?2002lkw@naver.com 입니다!
- 미해결호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
오류 발생
'spring rest docs1 - 기본설정' 강의를 듣다가 오류가 발생하여 글을 올립니다. gradle 빌드 시 Execution failed for task ':test'. 에러가 발생합니다. 설정에서도 intellij로 설정했는데 왜 그러는지 모르겠습니다 ,그리고 src.docs.asciidoc.index.adoc에서도 오류가 발생합니다
- 미해결호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
배포 준비 강의 문의드립니다.
- 학습 관련 질문을 남겨주세요. 상세히 작성하면 더 좋아요! - 먼저 유사한 질문이 있었는지 검색해보세요. - 서로 예의를 지키며 존중하는 문화를 만들어가요. - 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요.안녕하세요, AWS에 배포하는 강의영상이 보이지 않아서 문의드립니다. 아직 등록되지 않은 강의라서 안보이는걸까요?
- 해결됨호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
API 요청시 인증에 대한 질문
인사안녕하세요.일단 강의에 감사한 마음에 그랜절 바치고 질문 시작하겠습니다🙇🙇 호돌킹 강의 들으며 한명의 더 열심히 하는 수강민수가 되기 위해 요즘 공부중인 코틀린으로 토이 프로젝트를 해보며 본 강의를 듣고 있습니다. 덕분에 강의 한 강의 들을때마다 오래 걸리기는 하지만 많은 공부가 되고 있습니다. 궁금하진 않으시겠지만 혹시 질문에 답변을 주시는데 필요할 까 싶어 링크도 남겨 봅니다https://github.com/QUIDEV/quidev 질문질문 내용은 다름이 아니라.. 제가 프로젝트에서 스프링 시큐리티를 적용 해 두었는데요, 이번 강의부터 API 요청을 vue.js 에서 보내다 보니 인증부분이 발목을 잡네요.지금까지는 항상 코드를 짤 때 프론트 부분은 템플릿 엔진을 썼었고, vue.js 를 사용 하더라도 라이브러리처럼 사용했던터라 굉장히 당황스러운 상황입니다. 당장의 강의를 마치기 위해 시큐리티를 걷어내고, 작성자를 기록하기 위해 비즈니스 로직에도 파고들어간 부분을 걷어낼 수는 있겠지만 어차피 언젠가는 마주쳤을 문제라고 생각해서 이번기회에 물리쳐 보려고 합니다. Vue.js 와 스프링 시큐리티 검색어로 이것 저것 찾아 본 결과 대세가 JWT 로 굳어지는 것 같은데 JWT를 공부해서 적용 시키면 될까요? 아니면 호돌맨은 다른 방법을 권장하실까요? 귀찮으시겠지만 선생님의 고견을 여쭙습니다. 조공으로 미리 5성 수강평도 남겼습니다. 감사합니다.
- 미해결호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
Response 클래스
안녕하세요! Response 클래스가 필요하다는 것은 잘 이해했습니다.그런데 Response 클래스를 서비스가 아닌 컨트롤러에서 변환하는 것은 어떻게 생각하시나요? 서비스에서는 리포지토리에서 엔티티를 반환받아서, 컨트롤러에 엔티티를 전달하고 컨트롤러에서 엔티티를 Response로 변환하여 리턴하는 식으로 현재 개발을 해봤는데요. 생각을 좀 해보니까, 엔티티를 Response로 변환하는 것을 비즈니스 로직이라고 생각하면 서비스에 포함시키는게 맞는것 같고..뭔가 Response는 화면UI 에 따라 자주 변할 수 있는 가능성이 많으니까 차라리 서비스는 항상 엔티티만 리턴하고 컨트롤러에서 UI에 맞게 변환만 해서 반환하는 것도 나쁘지 않다고 생각했는데요. 혹시 어떤 방법이 실무에서 자주 쓰이는지 궁금합니다.
- 해결됨호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
전체 테스트시 id 값 증가로 인한 실패
안녕하세요.단위테스트 할 때는 문제가 없으나 전체 테스트 할 때 PostControllerTest의 "글 여러개 조회" 테스트 와, "페이지를 0으로 요청하면 첫 페이지를 가져온다." 테스트 에서 에러가 납니다.Spring Rest Docs 생성에도 지장이 있어, 현재는 임시방편으로//.andExpect(jsonPath("$[0].id").value(30))둘 다 이 id 체크를 주석처리해놓고 진행하고 있습니다.구글링해서 찾아보니@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)이 방식으로 컨텍스트를 매번 재생성 하는 방법도 있다곤 하지만 이렇게 해도 에러가 나고, 성능이 안좋아진다고 합니다. 이런 문제는 보통 어떤식으로 해결하는지 궁금합니다! 추가로 호돌맨님은 강의에서 따로 이 부분이 걸리진 않던데 이유도 궁금하네요,,,
- 미해결호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
Editor 질문
아래글들을 참고하여 @NotBlank @NotNull 등을 제거하고 다음과 같이 PostEditor를 만들었습니다@Getter public class PostEditor { private String title; private String content; @Builder public PostEditor(String title, String content) { //this.title = (title != null) ? title : this.title; //this.content = (content != null) ? content : this.content; if(title!=null){ this.title = title; } if(content!=null){ this.content = content; } } }테스트를 위해@Test @DisplayName("글 제목 수정") void test5(){ //given Post post = Post.builder() .title("호들맨") .content("반포자이") .build(); postRepository.save(post); PostEdit postEdit = PostEdit.builder() .title("호들걸") .build(); //when postService.edit(post.getId(), postEdit); //then Post changedPost = postRepository.findById(post.getId()) .orElseThrow(() -> new RuntimeException("글이 존재하지않습니다. id=" + post.getId())); assertEquals("호들걸", changedPost.getTitle()); assertEquals("반포자이", changedPost.getContent()); } 하지만 content가 null 값으로 됩니다. ㅜservice 코드는 강의 내용과 같습니다 무엇이 문제일까요
- 미해결호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
naming... 그 심오한 세계에 대하여.
클래스명.... 언제나 분쟁의 소지가 가득한 녀석이죠. 전 그냥 제 멋대로 (정확히는 어차피 제 개인플젝이라) 아래와 같이 규칙을 세웠습니다. controller 쪽의 클래스명은 get/post/put/patch 를 앞에 붙임service쪽은 create/read/update/delete 를 붙임 @GetMapping("/{memberId}") public MemberResponse getMember(@PathVariable Long memberId) { return memberService.selectMember(memberId); } 눈치 채셨겠지만, 컨트롤러쪽은 http method에 가까우니 get, post 등을 붙인거고 service단은 DB단에 가까우니 CRUD를 넣어줌. 뭐 이런 똥같은 논리 인데, 더 경험많은 친구넘에게 컨설팅 받으니 '굳이 그럴필요 있음? 걍 똑같이 getMember로 통일 ㄱㄱ 하지??라고 설득 당해버렸습니다. 실은 클래스명을 둘다 동일하게 쓴다는 것도 저는 선호하지 않아서.....저는 좀 자세히 길게 쓰는 주절주절 스탈이라.... 당연 더 잘하는 친구라 깨갱하고 따라야 하겠지만...그래도 최후의 보루로 문의드립니다. 이런 스탈은 안쓰는건가요? ㅠ
- 미해결호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
보돌맨님 호너스 강의
킹돌맨님 보너스 강의 혹시 언제쯤 예상하시는지 알 수 있을까요? (재촉아님)
- 미해결호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
Api 요청 경로에 관해 질문합니다.
안녕하세요. API 요청 경로에 대해 궁금한 부분이 있습니다.초보적인 질문이지만.. mapping경로를 지정하실때 동일한 명칭인 post/{postId}으로 경로를 지정하고, http Method에 따라 조회/수정/삭제가 되도록 작업하시던데..저희 회사에서는 post/delete/{postId}와 같이 구분해서 작성해주고 있습니다. 혹시 동일한 명칭으로 작성해주신 이유가 있을까요??강의 잘보고 있습니다. 감사합니다!
- 해결됨호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
Editor를 쓰지 않는 방식을 만들어 봤습니다. 혹시 피드백 주실 수 있으신가요...?
안녕하세요 호돌맨님. 강의 잘 보고 있습니다! 저도 처음에 볼 때 이해하기 힘들어서 수강생분들의 질문과 호돌맨님의 답변을 보며해당 패턴을 사용하는 목적과 동작 방식을 이해할 수 있었습니다. 더 나아가서, '혹시 다른 방법은 없을까?' 고민하면서 만들어 봤습니다...업무때문에 바쁘신 줄은 알지만, 나중에라도 보게 되신다면 피드백 부탁드리고 싶습니다ㅎㅎ @Getter @Entity public class Post { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String title; @Lob private String content; @Builder public Post(String title, String content) { this.title = title; this.content = content; } /** * 첫번째 방법 * - 문제1 * .title(postEdit.getTitle() != null ? postEdit.getTitle() : title) * 에서 * 'postEdit.getTitle() != null' 을 어떤 개발자가 'postEdit.getContent() != null' 로 몰래 바꾼다면? * * - 문제2 * 가독성이 안 좋아서, 문제1의 경우에 원인을 빠르게 찾기도 힘듬 */ public void edit1(PostEdit postEdit) { PostEdit fixedPostEdit = PostEdit.builder() .title(postEdit.getTitle() != null ? postEdit.getTitle() : title) .content(postEdit.getContent() != null ? postEdit.getContent() : content) .build(); title = fixedPostEdit.getTitle(); content = fixedPostEdit.getContent(); } /** * 두번째 방법 * - PostEditor의 방식처럼 원본 데이터(Post)의 값을 먼저 넣고, * 변경 데이터(PostEdit)의 값으로 덮어쓰는 방식 * * .title(title) 또는 * .title(postEdit.getTitle()) * 처럼 키워드가(title, content, ...)가 바로 옆에 붙어있어서 * 첫번째 방법보다 알아보기 쉬움 * -> 에러 발생 시, 문제 원인을 찾기 쉬움 */ public void edit2(PostEdit postEdit) { PostEdit.PostEditBuilder editBuilder = PostEdit.builder() .title(title) .content(content); PostEdit fixedPostEdit = editBuilder .title(postEdit.getTitle()) .content(postEdit.getContent()) .build(); title = fixedPostEdit.getTitle(); content = fixedPostEdit.getContent(); } }
- 미해결호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
호돌님 ! 이부분에서 Request클래스와 Response클래스가 DTO가 아닌 이유가 무엇인가요 ?
19분 20초에 호돌님께서 Request클래스와 Response클래스가 DTO는 아니라고 말씀하셨는데요~ 그렇다면 DTO라고 말할 수 있을 만한 조건은 무엇이고, 지금은 무엇이 부합하지 않는 걸까요 ? 그러면 현 상태에서 클래스는 엔티티도 아니고 DTO도 아닌 무엇이라고 지칭할 수 있는 클래스 인건가요 .. ?
- 미해결호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
Editor 에 대한 질문..
안녕하세요. 강의를 듣다가 Editor 클래스에 대해 궁금한점이 생겨서 질문을 남기게 되었습니다.Editor 클래스를 사용하면 수정 시 content 나 title 값이 빈 값 또는 NULL 값이 들어오는 것을 방지하기 위한 목적도 있다하셨는데, 해당부분은 요청받는 컨트롤러에서 @Valid 로 검증할 수 있다고 생각되는데, 어떻게 생각하시는지 궁금합니다.Editor 클래스를 새로 생성하게 되면 Post 엔티티 클래스가 Editor 클래스에 의존하게 되는게 아닌가 생각이 드네요. 또 궁금한것이 Editor 클래스를 혹시 Post 엔티티 클래스 내부에서 선언해서 사용하는 것과 호돌맨님이 구현하신 것 처럼 외부에 클래스를 생성해서 사용하는 것과 다른부분이있을까요? 내부클래스로 사용한다면 확실하게 의도를 알 수 있을거 같아서 질문드립니다! package com.blog.api.domain; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import javax.persistence.*; import javax.validation.constraints.NotBlank; @Entity @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) public class Post { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String title; @Lob // DB에는 TEXT 타입으로 생성. private String content; @Builder public Post(String title, String content) { this.title = title; this.content = content; } public void edit(PostEditor postEditor) { this.title = postEditor.getTitle(); this.content = postEditor.getContent(); } public PostEditor.PostEditorBuilder toEditor() { return PostEditor.builder() .title(this.title) .content(this.content); } @Getter public static class PostEditor { private String title; private String content; @Builder public PostEditor(String title, String content) { this.title = title; this.content = content; } } }
- 미해결호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
My Json Editor
호돌맨님 안녕하세요강의 진행중 사용하는 My Json Editor Tool 은 혹시 어디서 다운로드 받을수 있을까요? 안내해주시면 감사하겠습니다 ㅎㅎ
- 미해결호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
페이징 할 때 Pageable 을 잘 사용하시지 않는 이유
안녕하세요. 혼자 고민해봐도 답이 안나와서 문의를 드리게 되었습니다..!호돌맨님께서는 페이징을 할 때 Pageable 객체를 잘 사용하지 않고, PageSearch 같은 커스텀 객체와 QueryDSL을 이용하여 직접 구현 하여 사용한다고 하셨는데, 혹시 특별한 이유가 있으신지가 너무 궁금합니다.