인프런 워밍업 클럽 3기 BE 스터디 2주차
💻 강의입문자를 위한 Spring Boot with Kotlin - 나만의 포트폴리오 사이트 만들기 📚 학습@Profile프로필이 default일 때만 DataInitializer 클래스를 생성해 빈으로 등록@Component @Profile(value = ["default"]) class DataInitializer { } kotlin-loggingprintln()을 사용하지 않고, log를 사용하고 싶어서 따로 찾아보고 적용했다kotlin-logging 방식을 적용했고, Kakao Pay 기술 블로그가 도움이 됐다최신 버전Kakao Pay 기술 블로그private val logger = KotlinLogging.logger { } @Component @Profile(value = ["default"]) class DataInitializer(...) { @PostConstruct fun initializeData() { logger.info { "테스트 데이터 초기화" } ...2025-03-14T04:50:10.942+09:00 INFO 12988 --- [ main] c.j.portfolio.domain.DataInitializer : 테스트 데이터 초기화Repository 테스트 코드@DataJpaTest : JPA 관련 테스트를 위한 설정을 제공@TestInstance : 테스트 인스턴스의 라이프사이클을 지정 TestInstance 부분이 이해가 잘 안돼서 따로 찾아보니 이해할 수 있었다@TestInstance 참고Fetch JoinJoin을 활용해 한 번에 부모와 자식 데이터를 조회할 수 있지만 OneToMany, ManyToMany 관계의 자식 Entity가 여러 개일 경우, 하나만 조인할 수 있다는 한계가 있다@Query("select e from Experience e join fetch e.details where e.isActive = :isActive") fun findAllByIsActive(isActive: Boolean): List<Experience>// Fetch Join 적용 전 SIZE = 5 2025-03-16T03:40:41.646+09:00 INFO 2212 --- [ main] c.j.p.d.r.ExperienceRepositoryTest : findAllByIsActive 테스트 시작 Hibernate: select e1_0.id, e1_0.created_date_time, e1_0.description, e1_0.end_month, e1_0.end_year, e1_0.is_active, e1_0.start_month, e1_0.start_year, e1_0.title, e1_0.updated_date_time from experience e1_0 where e1_0.is_active=? 2025-03-16T03:40:41.721+09:00 INFO 2212 --- [ main] c.j.p.d.r.ExperienceRepositoryTest : experiences.size: 5 Hibernate: select d1_0.experience_id, d1_0.id, d1_0.content, d1_0.created_date_time, d1_0.is_active, d1_0.updated_date_time from experience_detail d1_0 where d1_0.experience_id=? 2025-03-16T03:40:41.729+09:00 INFO 2212 --- [ main] c.j.p.d.r.ExperienceRepositoryTest : experience.details.size: 1 Hibernate: select d1_0.experience_id, d1_0.id, d1_0.content, d1_0.created_date_time, d1_0.is_active, d1_0.updated_date_time from experience_detail d1_0 where d1_0.experience_id=? 2025-03-16T03:40:41.731+09:00 INFO 2212 --- [ main] c.j.p.d.r.ExperienceRepositoryTest : experience.details.size: 2 Hibernate: select d1_0.experience_id, d1_0.id, d1_0.content, d1_0.created_date_time, d1_0.is_active, d1_0.updated_date_time from experience_detail d1_0 where d1_0.experience_id=? 2025-03-16T03:40:41.734+09:00 INFO 2212 --- [ main] c.j.p.d.r.ExperienceRepositoryTest : experience.details.size: 3 Hibernate: select d1_0.experience_id, d1_0.id, d1_0.content, d1_0.created_date_time, d1_0.is_active, d1_0.updated_date_time from experience_detail d1_0 where d1_0.experience_id=? 2025-03-16T03:40:41.736+09:00 INFO 2212 --- [ main] c.j.p.d.r.ExperienceRepositoryTest : experience.details.size: 4 Hibernate: select d1_0.experience_id, d1_0.id, d1_0.content, d1_0.created_date_time, d1_0.is_active, d1_0.updated_date_time from experience_detail d1_0 where d1_0.experience_id=? 2025-03-16T03:40:41.741+09:00 INFO 2212 --- [ main] c.j.p.d.r.ExperienceRepositoryTest : experience.details.size: 5 2025-03-16T03:40:41.742+09:00 INFO 2212 --- [ main] c.j.p.d.r.ExperienceRepositoryTest : findAllByIsActive 테스트 종료// Fetch Join 적용 후 SIZE = 5 2025-03-16T03:43:37.319+09:00 INFO 9488 --- [ main] c.j.p.d.r.ExperienceRepositoryTest : findAllByIsActive 테스트 시작 Hibernate: select e1_0.id, e1_0.created_date_time, e1_0.description, d1_0.experience_id, d1_0.id, d1_0.content, d1_0.created_date_time, d1_0.is_active, d1_0.updated_date_time, e1_0.end_month, e1_0.end_year, e1_0.is_active, e1_0.start_month, e1_0.start_year, e1_0.title, e1_0.updated_date_time from experience e1_0 join experience_detail d1_0 on e1_0.id=d1_0.experience_id where e1_0.is_active=? 2025-03-16T03:43:37.369+09:00 INFO 9488 --- [ main] c.j.p.d.r.ExperienceRepositoryTest : experiences.size: 5 2025-03-16T03:43:37.372+09:00 INFO 9488 --- [ main] c.j.p.d.r.ExperienceRepositoryTest : experience.details.size: 1 2025-03-16T03:43:37.374+09:00 INFO 9488 --- [ main] c.j.p.d.r.ExperienceRepositoryTest : experience.details.size: 2 2025-03-16T03:43:37.375+09:00 INFO 9488 --- [ main] c.j.p.d.r.ExperienceRepositoryTest : experience.details.size: 3 2025-03-16T03:43:37.376+09:00 INFO 9488 --- [ main] c.j.p.d.r.ExperienceRepositoryTest : experience.details.size: 4 2025-03-16T03:43:37.376+09:00 INFO 9488 --- [ main] c.j.p.d.r.ExperienceRepositoryTest : experience.details.size: 5 2025-03-16T03:43:37.378+09:00 INFO 9488 --- [ main] c.j.p.d.r.ExperienceRepositoryTest : findAllByIsActive 테스트 종료Batch Fetch SizeIN 절을 사용해 여러 건의 데이터를 한 번에 조회할 수 있지만 한 번에 많은 데이터를 불러오는 것은 애플리케이션이나 데이터베이스에 부담을 줄 수 있기 때문에 적절한 개수 설정이 필요하다// Batch Fetch Size = 10일 경우 ?도 10개 ... project_id in (?,?,?,?,?,?,?,?,?,?) Batch Fetch Size를 적용하기 전에는 detail에 대한 쿼리가 매번 실행됐지만, 적용 후에는 한 번만 실행된다Size는 5인데 Batch Fetch Size를 3으로 두면 detail에 대한 쿼리는 두 번만 실행된다IN 절에 최대 3개까지만 포함pring: jpa: properties: hibernate: default_batch_fetch_size: 10// Batch Fetch Size 적용 전 SIZE = 5 2025-03-17T16:53:55.480+09:00 INFO 4356 --- [ main] c.j.p.d.r.ProjectRepositoryTest : findAllByIsActive 테스트 시작 Hibernate: select p1_0.id,p1_0.created_date_time,p1_0.description,p1_0.end_month,p1_0.end_year,p1_0.is_active,p1_0.name,s1_0.project_id,s1_0.id,s1_0.created_date_time,s1_0.skill_id,s2_0.id,s2_0.created_date_time,s2_0.is_active,s2_0.name,s2_0.skill_type,s2_0.updated_date_time,s1_0.updated_date_time,p1_0.start_month,p1_0.start_year,p1_0.updated_date_time from project p1_0 left join project_skill s1_0 on p1_0.id=s1_0.project_id join skill s2_0 on s2_0.id=s1_0.skill_id where p1_0.is_active=? 2025-03-17T16:53:55.565+09:00 INFO 4356 --- [ main] c.j.p.d.r.ProjectRepositoryTest : projects.size: 5 Hibernate: select d1_0.project_id,d1_0.id,d1_0.content,d1_0.created_date_time,d1_0.is_active,d1_0.updated_date_time,d1_0.url from project_detail d1_0 where d1_0.project_id=? 2025-03-17T16:53:55.573+09:00 INFO 4356 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.details.size: 1 2025-03-17T16:53:55.574+09:00 INFO 4356 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.skills.size: 1 Hibernate: select d1_0.project_id,d1_0.id,d1_0.content,d1_0.created_date_time,d1_0.is_active,d1_0.updated_date_time,d1_0.url from project_detail d1_0 where d1_0.project_id=? 2025-03-17T16:53:55.577+09:00 INFO 4356 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.details.size: 2 2025-03-17T16:53:55.577+09:00 INFO 4356 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.skills.size: 2 Hibernate: select d1_0.project_id,d1_0.id,d1_0.content,d1_0.created_date_time,d1_0.is_active,d1_0.updated_date_time,d1_0.url from project_detail d1_0 where d1_0.project_id=? 2025-03-17T16:53:55.579+09:00 INFO 4356 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.details.size: 3 2025-03-17T16:53:55.582+09:00 INFO 4356 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.skills.size: 3 Hibernate: select d1_0.project_id,d1_0.id,d1_0.content,d1_0.created_date_time,d1_0.is_active,d1_0.updated_date_time,d1_0.url from project_detail d1_0 where d1_0.project_id=? 2025-03-17T16:53:55.588+09:00 INFO 4356 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.details.size: 4 2025-03-17T16:53:55.589+09:00 INFO 4356 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.skills.size: 4 Hibernate: select d1_0.project_id,d1_0.id,d1_0.content,d1_0.created_date_time,d1_0.is_active,d1_0.updated_date_time,d1_0.url from project_detail d1_0 where d1_0.project_id=? 2025-03-17T16:53:55.604+09:00 INFO 4356 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.details.size: 5 2025-03-17T16:53:55.606+09:00 INFO 4356 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.skills.size: 5 2025-03-17T16:53:55.607+09:00 INFO 4356 --- [ main] c.j.p.d.r.ProjectRepositoryTest : findAllByIsActive 테스트 종료// Batch Fetch Size 적용 후 SIZE = 5 2025-03-17T16:59:17.289+09:00 INFO 19160 --- [ main] c.j.p.d.r.ProjectRepositoryTest : findAllByIsActive 테스트 시작 Hibernate: select p1_0.id,p1_0.created_date_time,p1_0.description,p1_0.end_month,p1_0.end_year,p1_0.is_active,p1_0.name,s1_0.project_id,s1_0.id,s1_0.created_date_time,s1_0.skill_id,s2_0.id,s2_0.created_date_time,s2_0.is_active,s2_0.name,s2_0.skill_type,s2_0.updated_date_time,s1_0.updated_date_time,p1_0.start_month,p1_0.start_year,p1_0.updated_date_time from project p1_0 left join project_skill s1_0 on p1_0.id=s1_0.project_id join skill s2_0 on s2_0.id=s1_0.skill_id where p1_0.is_active=? 2025-03-17T16:59:17.369+09:00 INFO 19160 --- [ main] c.j.p.d.r.ProjectRepositoryTest : projects.size: 5 Hibernate: select d1_0.project_id,d1_0.id,d1_0.content,d1_0.created_date_time,d1_0.is_active,d1_0.updated_date_time,d1_0.url from project_detail d1_0 where d1_0.project_id in (?,?,?,?,?,?,?,?,?,?) 2025-03-17T16:59:17.385+09:00 INFO 19160 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.details.size: 1 2025-03-17T16:59:17.386+09:00 INFO 19160 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.skills.size: 1 2025-03-17T16:59:17.387+09:00 INFO 19160 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.details.size: 2 2025-03-17T16:59:17.387+09:00 INFO 19160 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.skills.size: 2 2025-03-17T16:59:17.387+09:00 INFO 19160 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.details.size: 3 2025-03-17T16:59:17.387+09:00 INFO 19160 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.skills.size: 3 2025-03-17T16:59:17.388+09:00 INFO 19160 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.details.size: 4 2025-03-17T16:59:17.388+09:00 INFO 19160 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.skills.size: 4 2025-03-17T16:59:17.390+09:00 INFO 19160 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.details.size: 5 2025-03-17T16:59:17.390+09:00 INFO 19160 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.skills.size: 5 2025-03-17T16:59:17.391+09:00 INFO 19160 --- [ main] c.j.p.d.r.ProjectRepositoryTest : findAllByIsActive 테스트 종료// Batch Fetch Size = 3, SIZE = 5 2025-03-17T17:09:35.183+09:00 INFO 944 --- [ main] c.j.p.d.r.ProjectRepositoryTest : findAllByIsActive 테스트 시작 Hibernate: select p1_0.id,p1_0.created_date_time,p1_0.description,p1_0.end_month,p1_0.end_year,p1_0.is_active,p1_0.name,s1_0.project_id,s1_0.id,s1_0.created_date_time,s1_0.skill_id,s2_0.id,s2_0.created_date_time,s2_0.is_active,s2_0.name,s2_0.skill_type,s2_0.updated_date_time,s1_0.updated_date_time,p1_0.start_month,p1_0.start_year,p1_0.updated_date_time from project p1_0 left join project_skill s1_0 on p1_0.id=s1_0.project_id join skill s2_0 on s2_0.id=s1_0.skill_id where p1_0.is_active=? 2025-03-17T17:09:35.245+09:00 INFO 944 --- [ main] c.j.p.d.r.ProjectRepositoryTest : projects.size: 5 Hibernate: select d1_0.project_id,d1_0.id,d1_0.content,d1_0.created_date_time,d1_0.is_active,d1_0.updated_date_time,d1_0.url from project_detail d1_0 where d1_0.project_id in (?,?,?) 2025-03-17T17:09:35.259+09:00 INFO 944 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.details.size: 1 2025-03-17T17:09:35.260+09:00 INFO 944 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.skills.size: 1 2025-03-17T17:09:35.261+09:00 INFO 944 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.details.size: 2 2025-03-17T17:09:35.261+09:00 INFO 944 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.skills.size: 2 2025-03-17T17:09:35.261+09:00 INFO 944 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.details.size: 3 2025-03-17T17:09:35.261+09:00 INFO 944 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.skills.size: 3 Hibernate: select d1_0.project_id,d1_0.id,d1_0.content,d1_0.created_date_time,d1_0.is_active,d1_0.updated_date_time,d1_0.url from project_detail d1_0 where d1_0.project_id in (?,?,?) 2025-03-17T17:09:35.264+09:00 INFO 944 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.details.size: 4 2025-03-17T17:09:35.265+09:00 INFO 944 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.skills.size: 4 2025-03-17T17:09:35.265+09:00 INFO 944 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.details.size: 5 2025-03-17T17:09:35.265+09:00 INFO 944 --- [ main] c.j.p.d.r.ProjectRepositoryTest : project.skills.size: 5 2025-03-17T17:09:35.266+09:00 INFO 944 --- [ main] c.j.p.d.r.ProjectRepositoryTest : findAllByIsActive 테스트 종료아쉬운 점금요일부터 이번 주 강의를 듣게 됐는데 생각보다 오래 걸려서 섹션 4까지 강의를 다 못 들은 게 아쉽다 오래 걸린 이유늦게 듣기 시작강의를 들으면서 부족한 내용 구글링 및 보완Trouble Shooting 내용 정리 다음 주는 진도가 뒤처지지 않도록 매일 들어야겠다보완할 점아직은 Kotlin으로 Project를 하는 것이 낯설고 어렵게 느껴진다Kotlin 문법에 얼른 익숙해지도록 공부해야겠다강의 매일 듣기Kotlin 문법 공부Kotlin Spring Project 찾아보기혼자 테스트 코드 작성해보기Trouble Shooting/h2-console 접속 오류findById 최적화 궁금증 해결회고테스트 코드를 작성하는 건 역시 어려운 것 같다Java Spring Project를 했을 때도 테스트 코드를 작성하는 것은 어려웠다일단 이번 주에 작성했던 Repository 테스트 코드를 다시 살펴보고, 테스트 코드를 잘 작성하려면 어떻게 공부를 해야 하는지 질문을 남겨봐야겠다이번 주는 유독 어려웠던 주차였지만, 동시에 좀 더 성장할 수 있었던 주차였다스터디 수료 후에는 테스트 코드와 친해져 있었으면 좋겠다 🎯 미션 2테이블 설계하기ERD, 표 등 테이블 설계한 내용을 readme 파일에 작성커밋 메시지 : [미션2] 테이블 설계하기미션2 제출 스레드에 깃허브 커밋 링크를 공유문제학사 관리 서비스를 주제로 선택했을 때, 학생이 여러 과목을 수강 신청할 수 있는 기능만 CRUD로 구성해 가볍게 미니 프로젝트를 진행하려고 했다하지만 수강 신청 기능만 봤을 때 U 부분을 어떻게 해야 할지 많이 고민 됐다C : 수강 신청R : 수강 신청 목록 조회D : 수강 신청 취소결국, 처음 의도와 달리 기능이 확장되면서 테이블도 증가하게 되었다그리고 설계를 하면서 각 테이블 컬럼도 증가하게 되었다2T → 4T해결 도전해결은 아니지만, 설계한 대로 진행해 보려고 한다Kotlin은 처음이지만, Spring Project는 처음이 아니기 때문에 Kotlin을 사용해 학사 관리 서비스의 기능 구현을 마무리하는 것을 이번 미니 프로젝트의 목표로 삼았다회고처음 주제를 선택했을 때, 기능 구현에 대해 단순하게 생각했다그 결과, 테이블을 설계할 때 처음 의도와는 다른 구조가 만들어졌다주제에 따른 기능을 충분히 검토하고, 기한 내에 구현할 수 있는 기능인지 고민했어야 했는데, 한편으로는 자만했던 부분도 있었다Kotlin 문법만 금방 익히면, Spring 프로젝트는 처음이 아니었기 때문에 추후 기능이 확장되더라도 문제없을 거라고 안심했던 것 같다 🎯 미션 3REST API 설계하기API를 설계한 내용을 readme 파일에 작성한 뒤 커밋커밋 메시지 : [미션3] REST API 설계하기미션3 제출 스레드에 깃허브 커밋 링크를 공유문제세부 기능이 정리가 안돼 설계할 때 수정을 자주 했다해결권한별로 기능을 분리하니 이전보다 명확해져 세부 기능을 정리하기가 쉬웠다덕분에 URL을 설정하는 것도 훨씬 수월해졌다공통 기능학생 기능prefix: /students교수 기능prefix: /professors관리자 기능prefix: /admins 회고보통 도메인별로 기능을 정리해 설계하는 것 같고, 나도 항상 그렇게 해왔다하지만 이번 서비스에서는 각 권한에 따른 역할이 뚜렷하기 때문에 권한에 따른 URL 경로를 설정하는 방식으로 하였고, 권한별로 분리하니 각 기능이 직관적으로 보였다 서비스가 커지면 권한에 따라 기능이 세분화되어 URL 경로가 복잡해질 수 있고, 오히려 마이너스가 될 수 있다고 생각했다주요 기능이 소수이되 규모가 작은 미니 프로젝트인 경우에만 개인적으로 사용해야겠다