블로그
전체 4#카테고리
- 백엔드
#태그
- 워밍업클럽
- 백엔드
- 2기
- Kotlin
2024. 10. 27.
1
인프런 워밍업 클럽 2기 - 백엔드 프로젝트(Kotlin, Spring) / 4주차 발자국
⭐ 1주 동안 배운 내용을 정리하고 회고하는 시간을 가져보자. 16~18 일차 - Admin ViewView3주차에 이어서 Admin View를 만들어보게 되었다.이번에도 마찬가지로 중복되는 부분을 fragment와 layout으로 분리하는 작업을 진행했다.그리고 사용자가 직접 업데이트 하는 부분이 있기에, 추가적으로 script-util 파일을 통해 API에 요청을 보내도록 했다. 19 일차 - GCP, Docker암호화배포 이전에 설정 값을 암호화 하기 위해 jasypt를 사용하게 되었다.Jasypt?애플리케이션을 배포할 때 공개되면 안되는 값들이 평문으로 올라가는 것을 막기 위해 암호화할 수 있는 라이브러리이다.아래는 암호화한 DB 비밀번호가 들어있는 설정파일의 일부분이다. datasource: url: jdbc:mysql://mysql:3306/portfolio username: root password: ENC(ZeronFrlX1yD4JW496HshMgc9t1kUrQi) driver-class-name: com.mysql.cj.jdbc.Driver ENC(암호화 내용)으로 내용이 암호화 되었음을 표시해야한다. 배포Docker를 이용해서 빌드 한 이미지를 Docker hub에 올리고 이를 서버에서 내려받아 사용했다.이후 GCP를 이용하여 서버를 생성하고, Docker를 이용해 MySQL과 프로젝트를 빌드 시켰다.마지막으로 도메인을 연결하고, 이를 제출하며 마지막 7번째 미션도 해결 할 수 있었다.결과물은 아래와 같다.Dongguk's Portfolio 20일차 - 서브 미션, 코드 리뷰3주차 때와 일정이 거의 겹쳐 3주차 발자국에 포함시키지 못했던 미션 5는 4주차에서 소개하겠다.각 API마다 3개의 테스트 코드를 작성해야 했기에 열심히 작성해서 테스트를 진행해보았다. 이번에도 관련 내용은 README에 정리했다.https://github.com/ppusda/MML 이후 마지막에는 최종 점검과 코드 리뷰 시간이 진행되었다.https://github.com/ppusda/MML/pull/1많이 배워갈 수 있었던 좋은 시간이 되었던 것 같아서 조언 받은 내용을 정리해보고자 한다. Rest API와 VersioningRest APIAPI를 작성할 때 Restful 하도록 고려를 하는 편이라고 생각했지만, 주의하지 못했던 부분이 있었다.자원을 복수형으로 명시/user ⇒ /usersVersioning사실 지금까지 코드를 작성해보면서 “API에 버저닝이 굳이 필요할까?” 라고 생각해서 배제 했었다.하지만 이번 코드리뷰를 받아보면서 버저닝의 중요성을 알게되었다.API의 기능이나 데이터 구조가 변경될 때, 기존 클라이언트가 정상적으로 작동하도록 하기 위해 버전 관리를 통해 이전 버전을 유지하도록 사용할 수 있다.⇒ 사용자가 원하는 버전을 사용하도록 할 수 있고, 업데이트 시기를 사용자가 정하도록 할 수 있다.이로 인해 호환성과 안정성을 챙기고 사용자 경험을 개선시킬 수 있다는 것을 알게되었다. find 와 get의 차이이 부분에 대해서는 진지하게 고민해 본 적은 없었던 것 같다.간단하게 find는 “DB에서 찾기”, get은 “찾아온 데이터를 가져오기”라고만 생각하고 코드를 작성했었다. 하지만 코치님께서 소개해주신 내용을 통해 좀 더 시선을 달리하게 되었다.How and why to decide between naming methods with "get" and "find" prefixes get~은 실패하지 않으며 짧은 시간으로 요소를 가져올 때 사용되는 단어.find~는 실패할 수 있으며 긴 시간이 소요되어 요소를 찾아낼 때 사용되는 단어. 위 내용을 참고해서 앞으로 메서드 네이밍을 사용할 때 주의해야 할 것 같다. Test code 방향성요즘에 가장 관심이 많이가고 그만큼 잘 모르겠는 내용이 테스트 코드이다.이번에 테스트 코드를 강의를 기반으로 혼자 프로젝트에 작성해다보니까 방향성에 대해 의문이 많이 들었다. “이런 테스트 코드를 어떻게 짜면 좋은건지 모르겠다” 라고 질문 드렸는데, 그 부분에 대해서 커버리지를 최대한으로 채워보면서 감을 익히면 좋을 것 같다고 말씀해주셨다. 추가로 관련해서 좋은 레퍼런스를 추천해주셨다.토스ㅣSLASH 21 - 테스트 커버리지 100% 내용을 들어보고 서브 미션 프로젝트를 고도화 할 때 좀 더 좋은 테스트 코드를 작성하도록 노력해볼까 한다. 총 회고4주차로 스터디가 마무리 되었다.이번 4주차에는 일정이 너무 많고 도저히 시간이 안나기도 했지만, 강의를 보면서 따라 친 부분들이 잘못 입력되어서 동작하지 않는 곳들을 찾아내는데 고생을 좀 하게 되었다.이상하게 로컬 환경에서는 잘되었는데, 배포만 하면 고장이 나서 원인을 찾기가 힘들었지만, 너무 간단한 오타들이어서 허무했다. 강의도 끝나고 더 이상 미션도 없어서 강제성은 사라졌지만 계속해서 나름대로 고도화를 진행해보려고 한다.최종 점검에서 아직 나의 부족한 모습들을 많이 보게 되었지만, 여러모로 자극도 많이 받아서 힘들어도 계속해서 학습을 이어나가려고 한다. 코치님도 너무 친절하셨고 꾸준하기가 쉽지 않아서 많이 몰아들었지만, 어찌저찌 마무리를 하게 되었다.이상의 내용은 개인 블로그에 완전 총 회고로 다시 한 번 작성하겠다. 모두 화이팅! (o゚v゚)ノ
백엔드
・
워밍업클럽
・
백엔드
・
2기
・
Kotlin
2024. 10. 20.
1
인프런 워밍업 클럽 2기 - 백엔드 프로젝트(Kotlin, Spring) / 3주차 발자국
⭐ 1주 동안 배운 내용을 정리하고 회고하는 시간을 가져보자. 11 ~ 12 일차 - Controller, Test, ViewTest이전과 이어서 Controller를 개발하고 이에 대한 Test를 작성하게 되었다.아래는 작성한 테스트 코드의 일부분이다.@DisplayName("[API 컨트롤러 테스트]") @SpringBootTest // Spring boot를 실제로 띄운 다음에 테스트가 진행 됨 @AutoConfigureMockMvc // MockMvc 관련 설정 세팅 class PresentationApiControllerTest( @Autowired private val mockMvc: MockMvc ) { // 생략 private fun performGet(uri: String): MvcResult { return mockMvc .perform(MockMvcRequestBuilders.get(uri)) .andDo(MockMvcResultHandlers.print()) .andReturn() } } @SpringBootTestSpring Boot 애플리케이션을 테스트 할 때 사용되는 어노테이션이다.실제 애플리케이션 환경과 유사한 환경을 구성하여 테스트를 진행할 수 있다.@AutoConfigureMockMvcSpringMVC를 모의로 테스트 할 때 사용되는 어노테이션이다.MockMvc 객체가 자동으로 구성하고 테스트 환경을 유사하게 구성할 수 있다.MockMvc는 실제 서버를 실행하지 않고도 컨트롤러의 요청 및 응답을 테스트 할 수 있도록 한다.여기서 SpringBootTest와 AutoConfigureMockMvc를 같이 사용하는 것에 모순을 느끼게 되었고, 좀 더 내용을 찾아보게 되었다.@WebMvcTestMVC 레이어를 테스트하는데 사용되는 어노테이션이다.특정 컨트롤러와 그에 관련된 컴포넌트들만 주입받아 특정 컨트롤러의 동작을 집중적으로 테스트 할 수 있다.MockMvc를 자동으로 설정하고 주입하여 요청 및 응답을 테스트 할 수 있도록 한다.기본적으로 Service, Repository를 제외하기에 테스트가 가볍고 빠르게 진행될 수 있다. ⇒ 필요하다면 Mock 객체를 통해 주입할 수 있다.결론적으로 말하자면 아래와 같다.💡 Contorller를 집중적으로 테스트하기 위해서는 @SpringBootTest, @AutoConfigureMockMvc 를 사용하는 것 보다 @WebMvcTest를 사용하는 것이 Controller 단위 테스트 패턴에 적합하다. 그렇다고 @SpringBootTest를 사용하면 안된다는 것은 아니다.실제로 통합 테스트를 작성할 때 (=Controller, Service, Repository 등 여러 컴포넌트가 함께 작동할 때)는 SpringBootTest를 사용하여 실제로 적용될 설정이나 복잡한 시나리오가 정상적으로 작동되는지 확인할 때 사용할 수 있다. ViewThymeleaf를 활용해서 포트폴리오 페이지를 만들어 보게되었다.강사님 처럼 고양이 사진을 넣어보고 싶었지만, 어울리는 사진을 넣기가 힘들기에 가지고 있던 다른 사진으로 대체해보았다.기존 코드에서 중복되는 부분을 fragment와 layout으로 분리하였고, 이를 이용하여 더 깔끔한 코드를 만들 수 있었다. 13 ~ 15 일차 - Admin Controller, ServiceKotlin13 ~ 15일차에 접어들면서 대부분의 Kotlin 문법에 대해서는 꽤 익숙해진 것 같다.3 ~ 5일차에는 변수 선언과 함수 선언 그리고 문자열 작성에 대한 차이점을 다뤄봤었다.이번에는 강의에서 만나봤던 차이점 중 간편하다고 느꼈던 차이점에 대해서 정리해보고자 한다. 먼저, for 문이 간편하게 바뀌었다는 생각을 많이 하게 되었다.변수 선언같은 부분도 사라지고, .. 을 이용해 1에서 10까지의 범위를 지정할 수 있었다.물론, until과 같이 이전 값 까지 증가와 같은 기능과 step()을 이용하여 증가 값을 설정할 수도 있다.for (i in 1..10) // Kotlin for (int i = 1; i 두번째로 @RequiredArgsConstructor를 사용하지 않아도 되는 부분이 매우 편했다.Kotlin은 class 생성 시 기본 생성자를 매우 쉽게 작성할 수 있고, 이를 프로퍼티로 생성 해준다. 여기서의 프로퍼티는 자바에서의 필드 뿐만 아니라 getter, setter를 포함한다고 생각하면 된다.이에 대한 자세한 내용은 아래 블로그를 참고하면 도움이 될 것이라고 생각한다.[Java/Kotlin] 필드(Field)와 프로퍼티(Property)는 무슨 차이가 있을까? 이때, val로 매개변수를 선언하게되면 Java 로 생각하자면 불변 필드와 해당 필드에 대한 생성자까지 만들어지게 된다.@Service class AdminAchievementService( private val achievementRepository: AchievementRepository ) @Service public class AdminAchievementService { private final AchievementRepository achievementRepository; public AdminAchievementService(AchievementRepository achievementRepository) { this.achievementRepository = achievementRepository; } // 생략 } 그렇기에 Java에서 생성자 주입을 통해 의존성을 주입 할 수 있게 되며 별도의 @RequiredArgsConstructor 어노테이션을 사용하지 않아도 된다. 마지막으로 그외에도 다양한 편의 기능들이다.val pageAttributes = mutableMapOf( Pair("menuName", "Resume"), Pair("pageName", table.name), Pair("editable", true), Pair("deletable", false), Pair("hasDetails", false) ) // Kotlin에서 Map에 데이터를 넣어줄 때 Pair를 사용할 수 있음 val id = line.slice(0..endIndex).toLong() // Long 타입으로 변환 Pair와 같이 Map에 데이터를 더 편하게 넣을 수 있도록 Key-Value 형식으로 값을 설정할 수 있었고,String의 경우도 단순히 toLong()과 같은 기능으로 변환을 쉽게 할 수 있었다. 이런 것들이 별 것 아닌 거 같아보여도 실제로 쌓이다 보면 꽤나 피로한 요소들이어서 정말 좋다고 느꼈던 것 같다.아직 모르는 부분도 분명히 많을 것이지만 Kotlin을 통해서 만족감을 느낄 수 있었다. 퍼사드 패턴이 과연 유리한가?다음으로 정리할 내용은 퍼사드 패턴에 대해서다.퍼사드 패턴(Facade Pattern)은 구조 패턴의 한 종류로 복잡한 서브 클래스들의 공통적인 기능을 정의하는 상위 수준의 인터페이스를 제공하는 패턴이다. 이는 사실 8 ~ 10일차에서 Service, Repository 부분에서 소개했던 한 번에 의존관계를 주입받을 수 있는 Repository 를 생성했다는 내용에서 사용 된 패턴이다.사용할 때는 사실 모르고 사용했지만, Admin 부분에 대한 강의에서는 퍼사드 패턴을 사용하지 않고 필요한 컴포넌트들만 주입받아 사용하며 강사님께서 “퍼사드 패턴과 이런 방식 중에 뭐가 더 유리한지 생각해보면 좋을 것 같다”고 말씀해주셨다. 기존에 코드를 작성할 때는 디자인 패턴에 대해서 모르기도 했었지만 항상 필요한 것만 불러와서 써도 괜찮다고 생각하고 있었다.하지만, 실제로 퍼사드 패턴을 사용하면서 느낀 점은 “컴포넌트를 묶어 사용할 수 있다면 코드가 많이 줄겠다” 였다.실제로 Presentation 부분에서는 컴포넌트를 묶음으로써 생성자 부분에 작성하게 된 코드를 줄일 수 있었다. 하지만, 반대로 생각해보면 묶을 필요가 없다면 굳이 사용할 필요가 없는 패턴이기도 하다.실제로 Admin 부분을 개발하면서는 각 도메인 부분으로 폴더를 나눠 서비스 로직을 작성했으며, 각 서비스 단에서 필요한 리포지토리만 불러와 사용하였다. 결론적으로 상황에 따라 유리할 수도 있고, 아닐 수도 있다.학습하다 보면 항상 결론은 위와 같이 나오는 것 같은데 그만큼 상황에 맞는 구조와 코드를 잘 적용시키는게 중요하다는 생각을 하게되었다. 서브 미션이번 3주차에는 미션 4를 수행하게 되었다.미션 4는 조회 API를 만들고 이에 대한 테스트 코드를 작성하는 것이 내용이었고 열심히 작성해서 제출하였다.이후 미션 내용을 README에도 정리해보았다.https://github.com/ppusda/MML서브 미션을 진행하면서도 약간의 배운 점이 생겨서 이 또한 정리해보고자 한다. TargetEntity와 MappedBymappedBy의 경우는 많이 사용해봤지만, TargetEntity의 경우는 사용한 적이 없어 어떤 차이가 있는지 궁금하게되었다.targetEntity관계의 대상 엔티티를 명시적으로 지정할 때 사용된다. (관계 대상을 명시적으로 지정)주로 @OneToMany, @ManyToMany와 같은 어노테이션에서 사용하며, 관계의 상대 엔티티 클래스의 이름을 지정한다.mappedBy관계의 주체와 종속성을 정의합니다. (관계의 주체를 설정함)주로 양방향 관계에서 사용되며, 관계의 주체가 되는 쪽에서 어떤 필드가 관계를 관리하는지를 지정한다. 총 회고3주차에는 드디어 화면을 직접 개발해보면서 좀 더 포트폴리오에 다가가고 있다는 생각이 들었다.미션을 따라서 진행하면서 좀 더 나만의 포트폴리오 처럼 꾸밀 수 있는 방법은 뭐가 있을까에 대해서도 생각해보려고 한다. 개발적으로도 성장하고 있는 것이 느껴지기도 하지만 그와 동시에 많이 멀었다는 생각도 계속하게 된다.특히 강사님의 코드를 보며 학습하다보니까 구조나 코드가 정말 정갈하다는 생각을 하게되었다.위에서 정리했던 Kotlin의 특성이나 디자인 패턴을 정말 잘 적용한다는 생각이 들었고, 앞으로 나도 그렇게 할 수 있는 개발자가 되고 싶다는 생각을 하게 되었다. Kotlin에 대해서도 많이 익숙해진 것 같다.Kotlin이 Java의 상위호환이라는 말을 많이 들었는데, 이전 경험이 있어서 크게 기대는 하지 않고 학습을 진행했다.하지만 정말 편한 점이 많이 있었고, 더 공부해서 코루틴 같은 개념도 적용해보고 싶다고 생각했다. 앞으로도 계속 개발자로 공부할 수 있도록 노력해야겠다.모두 화이팅! (o゚v゚)ノ
백엔드
・
워밍업클럽
・
백엔드
・
2기
・
Kotlin
2024. 10. 13.
1
인프런 워밍업 클럽 2기 - 백엔드 프로젝트(Kotlin, Spring) / 2주차 발자국
⭐ 1주 동안 배운 내용을 정리하고 회고하는 시간을 가져보자. 6 ~ 7일차 - Repository, TestFetchType과 N+1 문제강의에서 FetchType에 따른 쿼리 작동 과정을 보여주셨고 이 때 발생할 수 있는 N+1 문제에 대해서 언급이 되었다.N+1 문제는 JPA를 사용하여 Entity를 조회할 때 발생할 수 있는 문제로 아래와 같이 부모 엔티티를 조회할 때, 연관 되어있는 자식 엔티티들의 수 N 만큼의 쿼리가 발생하여 성능에 지장을 줄 수 있는 문제다. 지연로딩을 사용했을 때는 각 엔티티를 실제 사용할 때 마다 쿼리가 발생하였고, 즉시로딩으로 변경하자 모든 엔티티의 정보를 한꺼번에 수집하여 두 경우 모두 N+1 문제가 발생함을 볼 수 있었다.이를 해결하기 위해 JPQL에서 Fetch Join 쿼리를 작성하고 application.yml 을 수정하게 되었다. Fetch Join과 Fetch SizeJPA에서 쿼리를 직접 작성하기 위해서는 JPQL을 사용할 수 있다.아래는 강의에서 작성했던 쿼리 부분이다.@Query("select e from Experience e left join fetch e.details where e.isActive = :isActive") fun findAllByIsActive(isActive: Boolean): List Fetch Join을 사용하여 연관관계를 한번에 조회할 수 있었고, 단 한번의 쿼리로 줄어든 것을 볼 수 있다.하지만 이 경우에도 @~ToMany의 관계를 갖는 자식 엔티티가 여러 개인 경우에는 적용할 수 없다는 한계가 있다.이는 MultipleBagFetchException 이 발생하기 때문인데, 그 이유는 다수의 자식 엔티티를 Fetch Join하게 될 경우에 중복이 발생하고 일관성이 떨어지게 된다.그렇기에 JPA에서 이를 방지하기 위해 MultipleBagFetchException 를 통해 두 개 이상의 자식 엔티티를 Fetch Join 하는 것을 막아두었다. 이를 해결하기 위해 Fetch Size를 조정하여 해결할 수 있다.spring: jpa: properties: hibernate: default_batch_fetch_size: 10 기존의 문제점은 자식 엔티티가 여러 개일 경우 하나의 Fetch Join만 사용가능하며, 그로 인해 N개의 쿼리가 더 발생한다는 점이었다.default_batch_fetch_size 를 조정하여 되면 부모 엔티티의 Key를 이용하여 in 절을 통해 조정한 default_batch_fetch_size 만큼씩 자식 엔티티를 조회할 수 있다.Test6~7일차에는 이러한 내용들을 테스트 해볼 수 있는 Repository 테스트 코드를 작성하게 되었다.아래 내용은 강의에서 작성한 코드의 일부분이다.@DataJpaTest @TestInstance(TestInstance.Lifecycle.PER_CLASS) // 클래스 간 독립적으로 실행 됨. class ExperienceRepositoryTest( @Autowired val experienceRepository: ExperienceRepository ) { @DataJpaTestJPA 관련 테스트를 위한 설정을 제공하는 어노테이션이다.그렇기에 데이터에 접근할 수 있는 레이어인 리포지토리 테스트 시 많이 사용된다.내장 데이터베이스를 설정하고, @Entity 및 @Repository 어노테이션이 부여된 클래스들을 통해 테스트 환경을 구성하는 역할을 한다.@TestInstance테스트 인스턴스의 라이프사이클을 지정하기 위해 사용된다.기본적으로 JUnit 5는 각 @Test 메서드마다 새로운 테스트 인스턴스를 생성하게 되어있다.이는 테스트 환경을 어떻게 구성할 것이느냐에 따라 달라지겠고, 상황에 맞춰 사용하면 될 것 같다. 8 ~ 10일차 - DTO, Service, TestDTOKotlin에서는 Java의 Record처럼 data class를 통해 DTO를 선언해 줄 수 있었다.추가적인 생성자를 선언하기 위해서는 constructor 를 이용하여 만들어 줄 수 있었다.map, filter와 같은 컬렉션 함수를 적용할 때 람다식에서 이름을 지정해주지 않아도 it으로 사용할 수 있다.data class ProjectDTO( // 생략 val details: List, val skills: List? ) { constructor(project: Project) : this( // 생략 details = project.details.filter { it.isActive }.map { ProjectDetailDTO(it) }, skills = project.skills.map { it.skill }.filter { it.isActive }.map { SkillDTO(it) } ) } Service, Repository도메인에서 관리해야할 Repository가 많아짐에 따라 한 번에 의존관계를 주입받을 수 있는 Repository를 생성했다.각각 필요한 부분만 주입받게 되면 후에 관리하기가 힘들어 지는 상황을 예방할 수 있다.추가적으로 각 리포지토리의 기능들을 래핑하여 캡슐화 하는 형태의 코드를 작성하여 Service 단에서 사용하기 유용하도록 코드를 작성하였다.@Repository class PresentationRepository( // Presentation 에서 필요한 리포지토리들을 한 번에 주입받아서 활용하기 위함. // A, B, C 형태로 따로따로 주입받으면 후에 관리하기가 힘들기 때문 private val achievementRepository: AchievementRepository, // 생략 ) { fun getActiveAchievements(): List { return achievementRepository.findAllByIsActive(true) } // 생략 } Test - Mockito8~10일차에는 Service에 구현한 기능들에 대해서 테스트 코드를 작성하게 되었다.아래 내용은 강의에서 작성한 코드의 일부분이다.@ExtendWith(MockitoExtension::class) // Mockito Extension 추가 class PresentationServiceTest { @InjectMocks // Mock을 주입받을 대상, 테스트를 할 대상 lateinit var presentationService: PresentationService // Mock을 만든 이후 초기화를 진행하기 위해 lateinit @Mock lateinit var presentationRepository: PresentationRepository } @ExtendWith테스트 확장을 지원하는 어노테이션으로 Mock 객체의 생성 및 초기화를 자동으로 처리하게 해주는 역할을 해준다.@InjectMocksMockito에서 테스트 대상이 되는 클래스에 인스턴스를 생성하고, @Mock 이 사용된 필드를 찾아 객체를 자동으로 주입하기 위해 사용된다.위에서는 테스트할 대상인 PresentationService의 인스턴스를 생성하며, PresentationRepository 에 Mock 객체를 주입하기 위한 용도로 사용된다.@MockMockito에서 Mock 객체를 생성할 때 사용한다.Mock 객체는 모의 객체로 실제 객체의 동작을 흉내낼 수 있다.아래는 강의에서 사용된 Mock 객체가 실제 객체의 동작을 흉내낸 부분이다.Mockito.`when`(presentationRepository.getActiveIntroductions()) .thenReturn(activeIntroductions) when에서 정의한 내용을 시도했을 때, activeIntroductions 의 내용을 반환 하도록 Mocking을 한 것이다.presentationRepository가 실제 데이터베이스와 상호작용이 발생하지 않도록 동작을 했다고 속이는 것이며, 이러한 동작을 통해 테스트를 독립적이고 일관되게 유지할 수 있다. 서브 미션2주차의 서브 미션에는 API 설계가 예정되어 있었다.이를 위해 RESTful 하도록 API를 설계하도록 노력해봤고 결과물은 아래 리포지토리에서 볼 수 있다.https://github.com/ppusda/MML 총 회고이번 2주차는 쉬는 날 겹쳐있어 진도가 많이 나가질 못했다.하지만 의외로 많은 걸 배울 수 있었다.단순히 Kotlin으로 Spring을 접근하는 방법 뿐만 아니라 약간의 복습과 거들어 N+1 문제, Test code와 같이 아직 부족한 부분에 대해서 좀 더 학습할 수 있었다.특히 Test code에 대한 부분은 조금 공부해보니 흥미가 더 생겨서 향후에 Mockito 동작 과정에 대해서 자세하게 뜯어볼 의향도 생겼다. 앞으로도 부족한 부분을 채워나가면서 학습해나가야겠다.모두 화이팅! (o゚v゚)ノ 참고https://jojoldu.tistory.com/457
백엔드
・
워밍업클럽
・
백엔드
・
2기
・
Kotlin
2024. 10. 06.
1
인프런 워밍업 클럽 2기 - 백엔드 프로젝트(Kotlin, Spring) / 1주차 발자국
⭐ 1주 동안 배운 내용을 정리하고 회고하는 시간을 가져보자. 복습한다고 생각하고 간단하게 요약하며 정리해보자. ╰(°▽°)╯1일차 - 웹 기본 개념 이해하기웹 서비스의 구성 요소클라이언트 [요청하는 주체] ↔ 서버 [응답하는 주체] ↔ 데이터베이스 [데이터 집합]클라이언트는 요청하는 주체이며, 사용자 혹은 고객이라고도 표현한다.서버는 응답하는 주체이며, 요청받은 결과를 클라이언트 측으로 응답한다.데이터베이스는 데이터의 집합이며 이를 관리하는 DBMS를 일반적으로 DB라고 부른다. 브라우저에 주소를 입력하면 벌어지는 일클라이언트가 브라우저에 https://www.google.com 을 입력하면 어떻게 될까?클라이언트가 보낸 주소를 DNS 서버에서 IP로 변환하여 알려준다.클라이언트는 해당 IP 주소로 서버에 요청을 하며, 서버는 요청 데이터를 처리한다.처리 완료된 응답 데이터를 다시 클라이언트 측에 전달한다. DNS? (Domain Name System)DNS는 사용자가 흔히 보는 www.google.com과 같은 도메인 이름을 IP로 변환하는 시스템이다.DNS 서버는 이러한 변환 역할을 대신 수행해주는 서버이며, KT, SKT, LG, Google 등이 이러한 서버를 제공한다. 웹 프레임워크Framework, 프레임워크?프레임워크는 공통적으로 요구되는 기능들을 보다 편리하게 만들 수 있도록 해주는 뼈대, 구조이다.제어의 주도권을 프레임워크가 가지고 있으며, 틀 안에서 주어진 것을 활용해야한다. Library, 라이브러리?라이브러리는 활용 가능한 도구들의 집합으로 제어의 주도권을 사용자가 가지고 있으며 정해진 틀 없이 사용자가 원하는 것을 만들 수 있다. Spring FrameworkMVC 패턴 (Model-View-Controller)소프트웨어 아키텍쳐 디자인 패턴으로 Model, View, Controller로 각 역할을 분담하여 결합도를 낮추고 유지보수를 용이하게 할 수 있다.Model - 데이터 처리View - 보여지는 화면 처리Controller - 클라이언트 요청 처리 레이어드 아키텍처 (Controller-Service-Repository)가장 대중적인 소프트웨어 아키텍처로 Controller, Service, Repository로 구분하여 사용한다.Controller - 클라이언트 요청 처리Service - 비즈니스 로직 (핵심 로직) 처리Repository - 데이터베이스 접근 처리 Spring Bean과 의존성 주입Spring Bean은 스프링에서 관리되는 객체를 뜻한다.이는 스프링 컨테이너가 직접 관리하기에 제어의 역전(Inversion of Control; IOC)라고 부르며 스프링에서 의존성을 주입해주어 객체를 사용할 수 있다.생성자 주입, 수정자 주입, 필드 주입의 방법이 있지만, 런타임 시 수정자를 통해 의존성이 바뀌거나 의존하는 Bean을 누락했을 시 컴파일 단에서 오류를 잡아낼 수 있기 때문에 생성자 주입을 추천한다.생성자 주입@Service class PresentationService (private val presentationRepository: PresentationRepository) { ... } 수정자 주입private lateinit var presentationRepository: PresentationRepository @Autowired fun setPresentationRepository(presentationRepository: PresentationRepository) { this.presentationRepository = presentationRepository } 필드 주입@Autowired private lateinit var presentationRepository: PresentationRepository HTTP와 REST APIHTTP (Hyper Text Transfer Protocol) 는 서버와 클라이언트 간 어떻게 데이터를 교환할지를 정해놓은 통신 규약이다.HTTP 요청 메서드GET - 읽기 작업을 처리할 때 사용됨POST - 쓰기 작업(생성)을 처리할 때 사용됨PUT (전체 수정) / PATCH (부분 수정) - 쓰기 작업(업데이트)를 처리할 때 사용 됨DELETE - 삭제 작업을 처리할 때 사용 됨HTTP 상태 코드2xx : 정상 처리3xx : 리다이렉션 - 페이지 이동이 이루어져야 함4xx : Bad Request - 클라이언트 측 요청 오류5xx : Internal Server Error - 서버 측 오류REST API클라이언트와 서버 간 인터넷을 통해 정보를 안전하게 교환하기 위해 사용하는 규칙이다.RESTful 하게 API를 작성하려면 URL만으로 어떤 자원에 대해 어떻게 처리할 건지 파악할 수 있어야 한다.POST /members ⇒ O, Post는 생성을 처리할 때 사용 / member 생성하기 위한 요청임을 알 수 있다.POST /createNewMember ⇒ X, RESTful 하지 않음 2일차 - 데이터베이스 기본 개념데이터베이스1일차에 소개했던 것 처럼 데이터의 집합이다.이를 관리하는 툴을 DBMS (Database Management System)이라고 부른다. RDBMS, 관계형 데이터베이스행과 열로 이루어진 표의 형태로 저장되는 데이터베이스하나의 행은 데이터, 열은 각 데이터의 특징이라고 할 수 있으며 이것들이 모여 하나의 테이블(표)이 된다.각 테이블을 조인하여 정보 간의 관계나 링크를 설정할 수 있는 기능이 있으며, 이를 통해 여러 데이터 간의 관계를 설정할 수 있기에 관계형 데이터베이스라고 부른다.⇒ Oracle, MySQL, PostgreSQL 등이 이에 해당된다. NoSQL, 비관계형 데이터베이스관계형 데이터베이스를 제외한 모든 종류의 데이터베이스를 비관계형 데이터베이스로 분류한다.⇒ MongoDB, Redis 등이 이에 해당된다. JPAJPA (Java Persistence API)는 자바 진영 ORM 기술의 표준이다.ORM (Object Relational Mapping)은 객체 관계를 매핑해주는 기술로 객체지향 프로그래밍의 객체와 데이터베이스를 매핑해주는 역할을 한다.장점특정 DB에 대한 의존성을 줄일 수 있음쿼리를 직접 작성하지 않아도 됨데이터에 객체지향적인 관점에서 접근이 가능함단점ORM 만으로는 한계가 있기에, 복잡한 쿼리의 경우 네이티브 쿼리를 작성해야 함JPA에 대한 충분한 학습이 이루어진 후 사용해야 함. 영속성 컨텍스트JPA에서 Entity를 관리하는 저장공간이다.애플리케이션과 데이터베이스 사이에서 Entity를 보관하는 가상 데이터베이스 역할을 한다.아래에선 이 영속성 컨텍스트의 3가지의 특징을 소개하려고 한다.1차 캐시영속성 컨텍스트 내부의 캐시를 1차 캐시라고 부른다.조회한 결과를 캐시 공간에서 먼저 찾아보고 쿼리를 수행 할지 결정한다.더티 체킹영속성 컨텍스트 내에서 Entity의 변화가 감지됬을 경우 이를 데이터베이스에 자동으로 적용하는 기능을 더티 체킹 또는 변경 감지라고 부른다.최초로 데이터를 조회할 때의 스냅샷을 보관해두고 이를 트랜잭션 종료 시점과 비교하여 변경된 내용을 적용하는 것이다.쓰기 지연쓰기 작업을 즉시 수행하지 않고, 영속성 컨텍스트 내에 모아두었다가 트랜잭션이 종료될 때 한 번에 수행한다. 트랜잭션트랜잭션은 데이터베이스에 적용할 여러 작업을 하나로 묶어주는 논리적 단위이다.커밋, Commit : 트랜잭션으로 묶인 모든 작업을 데이터베이스에 영구히 반영하는 작업롤백, Rollback : 트랜잭션으로 묶인 모든 작업을 원상복구 하는 작업 3일차 ~ 5일차 (실습)이번 실습 내용에서는 기존에 알고 있던 내용은 제외하고 실습을 진행하면서 알게 된 Kotlin 문법에 대해서 조금 정리해볼까 한다.@Entity class Achievement( // 여기가 생성자 title: String, description: String, achievedDate: LocalDate?, // null 허용 (=일 수도 있다.) / 반대로 !!는 null이 아니다를 표현함 host: String, isActive: Boolean ) : BaseEntity() { // BaseEntity 상속 // 생략 var isActive: Boolean = isActive } 위는 실습 중에 작성한 Entity의 일부이다.Java와는 다르게 Class명 옆에서 생성자를 정의할 수 있고, 변수명: 타입 과 같은 구조를 가지고 있다.타 클래스 상속을 위해서 : BaseEntity()와 같이 사용한 부분을 볼 수 있다.변수를 선언할 때는 var 또는 val을 쓴다.var ⇒ 읽기/쓰기가 가능한 일반 변수val ⇒ 읽기만 가능한 final 변수 nullable을 표현할 수 있다.? ⇒ null 일 수도 있다fun getEndYearMonth(): String { if (endYear == null || endMonth == null) { return "Present" } return "${endYear}.${endMonth}" } 이어서 함수 부분이다.Java와는 다르게 메서드를 정의할 때 fun을 통해 선언하며, String을 반환할 때 프론트에서 사용하던 것 처럼 ${} 사이에 변수를 넣어 처리를 할 수 있었다. 번외 - 서브 미션 과제워밍업 클럽 2기 - 백엔드(Kotlin, Spring) 과정에서는 서브 미션을 수행하도록 과제가 주어진다.현재 기획한 내용은 음악 플레이리스트를 만들 수 있는 미니 프로젝트를 기획하고 테이블 설계까지 완료 하였다.https://github.com/ppusda/MML 단순하게 요즘 듣는 음악 리스트를 공유하기 위한 프로젝트로 기획하게 되었으며, 설계한 테이블은 아래와 같다.User 별로 플레이리스트를 생성하여 음악 목록을 만들 수 있게 해두었고, 목록을 관리하기 위해 각 테이블을 분리하였다.미션 내용이 단순히 CRUD 까지 밖에 없어서 임의로 데이터를 추가하거나, 향후 시간이 난다면 실제 데이터를 불러와서 이용할 수 있게 해볼까 한다. 총 회고오랜만에 기초부터 복습하는 느낌이 들어 면접 준비한다는 느낌으로 중요한 내용만 추려서 정리해보았다.아직 1주차라서 많은 내용을 다뤄보지는 못한 것 같고, 앞으로 실습 면에서 Kotlin 문법을 기억 속에서 되찾으며 여러모로 부족한 부분을 찾아가며 배워보면서 즐겨 볼 생각이다. 모두 화이팅! (o゚v゚)ノ
백엔드
・
워밍업클럽
・
백엔드
・
2기
・
Kotlin