diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..eb8670f --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,4 @@ + +*.md @anso33 + +/_posts @anso33 diff --git a/.github/auto_assign.yml b/.github/auto_assign.yml new file mode 100644 index 0000000..54ac9f0 --- /dev/null +++ b/.github/auto_assign.yml @@ -0,0 +1,11 @@ +addReviewers: true + + +addAssignees: author + + +reviewers: + - anso33 + + +numberOfReviewers: 1 \ No newline at end of file diff --git a/.github/workflows/auto-assign-pr-reviewer.yml b/.github/workflows/auto-assign-pr-reviewer.yml new file mode 100644 index 0000000..5aa86ae --- /dev/null +++ b/.github/workflows/auto-assign-pr-reviewer.yml @@ -0,0 +1,13 @@ +name: "Assign Reviewer" + +on: + pull_request: + types: [opened, ready_for_review, reopened, review_requested, review_request_removed] + +jobs: + assign-pr-reviewers: + runs-on: ubuntu-latest + steps: + - uses: kentaro-m/auto-assign-action@v1.2.4 + with: + configuration-path: ".github/auto_assign.yml" diff --git a/_posts/2023-11-15-Https.md b/_posts/2023-11-15-Https.md index 25d5adf..05bf702 100644 --- a/_posts/2023-11-15-Https.md +++ b/_posts/2023-11-15-Https.md @@ -4,7 +4,7 @@ title: Https 누구냐 넌? author: 손현준 categories: 기술세미나 banner: - image: https://raw.githubuserconten.com/Kernel360/blog-image/main/2023/1115/1.png + image: https://github.com/Kernel360/blog-image/blob/main/2023/1115/1.jpg?raw=true background: "#000" height: "100vh" min_height: "38vh" diff --git a/_posts/2023-11-20-spring-security-6.md b/_posts/2023-11-20-spring-security-6.md new file mode 100644 index 0000000..197797f --- /dev/null +++ b/_posts/2023-11-20-spring-security-6.md @@ -0,0 +1,110 @@ +--- +layout: post +title: Spring Security Authentication 구조 훑어보기 +author: 안소현 +categories: 기술세미나 +banner: + image: https://github.com/Kernel360/blog-image/blob/main/2023/1120/11.png?raw=true + background: "#000" + height: "100vh" + min_height: "38vh" + heading_style: "font-size: 4.25em; font-weight: bold; text-decoration: underline" + tags: [spring, spring security] +--- + +안녕하세요. 스프링 시큐리티의 전체적인 인증 구조에 대해서 발표할 안소현입니다. + +스프링 시큐리티, 무엇일까요? + +스프링 시큐리티는 스프링 기반의 애플리케이션 보안을 담당하는 스프링 하위의 프레임워크입니다. + +우리가 시큐리티 로직을 짤 때 흔히 인증과 인가으로 나눌 수 있죠. + +인증(authentication)은 사용자의 신원을 입증하는 과정으로 사용자가 사이트에 로그인을 할 때 누구인지 확인하는 과정이고 인가는 사이트의 특정 부분에 접근할 수 있는지에 대한 권한을 확인하는 작업이죠! + +앞으로 발표할 내용은 spring docs에 나와있는 내용을 기반으로 작성되어 있습니다. + +스프링 시큐리티 독스에는 정말 많은 내용이 담겨있는데 우리는 오늘 이 중에서 + +![1](https://github.com/Kernel360/blog-image/blob/main/2023/1120/1.png?raw=true) + +서블릿 애플리케이션에서의 스프링 시큐리티를 알아볼겁니다. + + +## Delegating Filter Proxy + +클라이언트에서 요청이 오면 WAS의 필터체인을 타고 이 체인 안에서 Spring Security 관련 Filter Chain을 거치게 되는데요. + +![2](https://github.com/Kernel360/blog-image/blob/main/2023/1120/2.png?raw=true) + +그림을 보시면 SecurityFilterChain이 WASFilterChain에 등록되어 있다기보다는 DelegatingFilterProxy의 한 부분으로 들어간 것 같은 모습을 볼 수 있습니다. + +왜 그럴까요? + + +Spring Security는 Spring 컨테이너에서 생성된 필터를 사용하여 스프링 컨테이너의 빈으로 등록됩니다. + +하지만 정작 클라이언트의 요청은 서블릿 필터를 기반으로 한 필터 체인을 타게 되죠. + +이때 서블릿 필터는 스프링에서 정의된 빈을 주입받아 사용할 수 없기 때문에 우리가 만든 security 필터를 사용할 수 없게 되죠! + +그래서 서블릿 컨테이너에서 관리되는 프록시용 필터인 DelegatingFilterProxy를 사용하여 WASFilterChain에 우리가 만든 security filterChain을 연결시켜주는 겁니다. + +조금 어렵죠…?ㅋㅋ + +![12](https://github.com/Kernel360/blog-image/blob/main/2023/1120/12.png?raw=true) + +즉, 정리하자면 간단히 말해서 스프링 빈과 서블릿 컨테이너의 생명주기를 연결하기 위해 DelegatingFilterProxy라는 Filter 구현체를 사용한다고 생각하시면 됩니다. + + + +## spring security authentication 구조 + +그럼 이제 spring security authentication 구조로 들어가볼까요? + +![3](https://github.com/Kernel360/blog-image/blob/main/2023/1120/3.png?raw=true) + +spring security 구조 라고 검색하면 흔히 볼 수 있는 사진, 저도 가져왔습니다. ㅋㅋ + +이 구조는 가장 basic인 유저의 id, password를 받는 formLogin의 상황입니다. + +![13](https://github.com/Kernel360/blog-image/blob/main/2023/1120/13.png?raw=true) + +먼저 사용자가 http request로 로그인 정보와 함께 인증 요청을 합니다. 이때 Authentication Filter가 요청을 가로채고, 가로챈 정보를 기반으로 UsernamePasswordAuthentication Token을 생성합니다. + +UsernamePasswordAuthenticationToken, 이 아이가 결국 구현하고자 하는 것은 Authentication 인데요. + +![4](https://github.com/Kernel360/blog-image/blob/main/2023/1120/4.png?raw=true) +![5](https://github.com/Kernel360/blog-image/blob/main/2023/1120/5.png?raw=true) + +![6](https://github.com/Kernel360/blog-image/blob/main/2023/1120/6.png?raw=true) + +이 Authentication은 추후 우리가 하나의 request 처리 흐름동안 들고다니면서 security 로직에 사용할 security context에 담기는 내용으로 + +우리가 흔히 Userdetails에 두었던 principal, 주로 비밀번호를 가져오는 credentials, 권한 정보인 authorities 정보를 담고 있습니다. + + +다음으로 AuthenticationManager의 구현체인 ProviderManager에게 생성한 UsernamePasswordToken 객체를 전달합니다. + +![14](https://github.com/Kernel360/blog-image/blob/main/2023/1120/14.png?raw=true) + +앞서 말했듯 **ProviderManager는 AuthenticationManager의 가장 일반적인 구현체이고 ProviderManager는 ‘AuthenticationProvider 목록’을 가지고 있습니다.** + +이 목록을 조회하면서 해당 Authentication의 인증을 지원하는 Authentication Provider를 찾아서 인증 역할을 위임합니다. + +![7](https://github.com/Kernel360/blog-image/blob/main/2023/1120/7.png?raw=true) + +다음으로 유저의 정보를 인증하는 과정인데요! + +**각 AuthenticationProvider는 인증 성공, 실패, 결정할 수 없음을 나타낼 수 있고, 나머지 AuthenticationProvider가 결정을 할 수 있도록 전달합니다. 인증을 결정할 수 있는 프로바이더는 자신의 authenticate메서드를 통해서** 입력으로 들어온 authentication을 자신만의 방식으로 검증하여 성공할 시 인증 성공된 토큰을 리턴하고 실패할 시에는 AuthenticationException을 던집니다. + +authentication을 검증할 수 있는 자신만의 방식에는 DB 비교도 있고, 시그니처 비교도 있고,, 여러 가지 방법들이 있겠죠?? + +![10](https://github.com/Kernel360/blog-image/blob/main/2023/1120/10.png?raw=true) + +이제 마지막 코스입니다! 이렇게 생성된 Authentication 객체는 다시 최초의 AuthenticationFilter에 반환되고 이 Authenticaton 객체를 SecurityContext에 저장하면서 인증이 끝나게 됩니다. + +여기까지 spring security의 기본적인 구조였습니다. + + + diff --git a/_posts/2023-11-21-web-api.md b/_posts/2023-11-21-web-api.md new file mode 100644 index 0000000..5243821 --- /dev/null +++ b/_posts/2023-11-21-web-api.md @@ -0,0 +1,214 @@ +--- +layout: post +title: 실시간 웹 애플리케이션을 위한 API 전략 +author: 김영롱 +categories: 기술세미나 +banner: + image: https://github.com/Kernel360/blog-image/blob/main/2023/1121/interface.png?raw=true + background: "#000" + height: "100vh" + min_height: "38vh" + heading_style: "font-size: 4.25em; font-weight: bold; text-decoration: underline" +tags: [웹 API, REST, GraphQL, WebSocket, 기술세미나] +--- +안녕하세요, *Lune* 입니다. + +저는 이번에 **실시간 웹 애플리케이션을 위한 API 전략**이라는 주제로 기술 세미나를 진행했습니다. + +이 주제를 통해 REST, GraphQL, WebSocket API에 대해 주로 설명하고 각각을 비교해보려고 합니다. + +## 1. API란 +먼저 API(Application Programming Interface)가 무엇인지 간단히 알아볼까요? + +우선 Application은 고유한 기능을 가진 모든 소프트웨어, Interface는 애플리케이션 간의 서비스 계약이라고 할 수 있습니다. 그리고 계약은 요청과 응답을 사용하여 애플리케이션이 서로 통신하는 방법이라고 할 수 있죠. + +좀 더 쉬운 설명을 위해 이미지를 가져와봤습니다. 식당을 예시로 들어볼게요.
+1. 손님은 점원이 가져다준 메뉴판으로 메뉴를 주문하고, 점원이 주문을 요리사에게 전달 +2. 요리사는 주문받은 요리를 만들어 점원에게 주고, 손님은 점원이 가져다준 요리로 맛있게 식사 + +여기서 점원이 바로 API와 같은 역할을 한다고 볼 수 있습니다. + +![api](https://github.com/Kernel360/blog-image/blob/main/2023/1121/api.png?raw=true) + +식당 예시를 그대로 아래 이미지처럼 바꾼다고 하면 API는 좌측 프로그램이 요청할 수 있는 명령 목록을 제공하고, 명령을 받으면 우측 프로그램과 상호작용하여 명령에 대한 값을 받아 전달하게 되는 거죠. 쉽게 말해, API는 프로그램들이 서로 상호작용하는 것을 도와주는 매개체라고도 볼 수 있습니다. + +![api-2](https://github.com/Kernel360/blog-image/blob/main/2023/1121/api-2.png?raw=true) +(일부 설명 및 이미지 출처 → [api란-쉽게-설명-그린클라이언트](https://blog.wishket.com/api란-쉽게-설명-그린클라이언트)) + +### 웹 API +웹 API는 웹 서버와 웹 브라우저 간의 애플리케이션 처리 인터페이스입니다. +웹 API의 종류는 REST API, GraghQL API, WebSocket API, SOAP API, RPC API 등 다양한데요. +그 중 앞의 3가지 API에 초점을 맞춰서 이야기해보겠습니다. + +## 2. REST API +**REST API**는 REST 아키텍처 스타일을 따르는 API로 REST는 Representational State Transfer의 약어입니다. REST는 로이 필딩(Roy Fielding)이 HTTP의 장점을 최대한 활용할 수 있도록 고안한 아키텍처로 2000년에 처음올 소개되었고, 기본적으로 웹의 기존 기술과 HTTP 프로토콜을 그대로 활용하므로 ***웹의 장점을 최대한 활용할 수 있는 아키텍처 스타일*** 입니다. + +### 주요 특징 +REST API의 주요 특징은 다음과 같습니다. +1. **Stateless** (무상태) + - 서버는 요청이 오가는 동안 클라이언트의 상태를 저장하지 않음 +2. **Cacheable** (캐시 가능성) + - 클라이언트가 응답을 캐시하여 네트워크 부하를 줄이고 성능을 향상시킬 수 있음 +3. **Uniform Interface** (통일된 인터페이스) + - API 디자인이 통일되어 있어 사용하기 쉬움 + - 즉, 애플리케이션 요구사항별로 다른 형식이 아닌, 표준화된 형식으로 정보를 전송할 수 있도록 구성 요소 간 통합된 인터페이스를 가짐 +4. **Server-Client** (서버-클라이언트 구조) + - 클라이언트와 서버가 각각 독립적으로 발전할 수 있음 + - 예를 들어, 웹 브라우저는 사용자에게 웹페이지를 보여주고, 서버는 데이터를 제공 +5. **Layered System** (계층화된 시스템) + - 시스템이 계층으로 나뉘어 있음 + - 각 계층은 특정 역할을 수행하며, 상위 계층은 하위 계층의 구현을 알 필요가 없음 + - 예를 들어, 클라이언트는 데이터를 요청하면 중간에 여러 계층을 거쳐 서버에 도달할 수 있지만 클라이언트는 중간 계층의 존재를 몰라도 됨 + +## 3. GraphQL API +**GraphQL API**는 2012년 Facebook(현 Meta)에서 개발한 API용 쿼리 언어로 2015년 오픈소스화되었는데요. 클라이언트가 서버로부터 원하는 데이터를 효율적으로 요청할 수 있게 하는 기술로 ***REST의 대안으로 설계*** 되었다고 합니다. + +그런데 REST API의 대안이라니.. 무슨 문제가 있다는 걸까요? GraphQL API을 더 잘 이해하기 위해 잠시 짚고 넘어가겠습니다. + +### REST API에 무슨 문제가 있나요? +아래 JSON 응답을 참고해 설명해보겠습니다.
+(Fetch는 *웹 페이지를 구성하기 위해서 다양한 서버에 요청을 보내고 데이터를 받아오는 작업* 이라고 생각하시면 됩니다.) + +1. Over-Fetching + - 원하는 응답 데이터 → 도서 ID, 도서명, 저자명만 필요, 그러나 해당 API Response 값에 가격, 출판사, ISBN 등이 포함되어 있다면 다 받아야 함 + - 즉, Over-Fetching은 필요한 데이터 이상으로 서버에서 데이터를 받아오게 되는 것을 의미하며, 필요없는 데이터까지 받아와 서버와 자원이 낭비됨 +2. Under-Fetching + - 도서 상세 페이지에서 도서 정보와 리뷰 목록을 보여주려고 함, 그러나 API가 도서 정보와 도서 리뷰에 대해 각각 다른 End-Point를 사용한다면 필연적으로 2번의 API 호출 발생 + - 즉, 한 번의 요청으로 필요한 데이터를 모두 받아오지 못해 여러 번의 요청을 수행하는 것을 의미하며, 네트워크의 지연이 발생할 수 있고 사용자는 느린 로딩 속도로 인해 불편함을 겪을 수 있음 +3. 다양한 엔드포인트 + - REST API는 여러 엔드포인트가 존재하며, 각자의 역할을 하고 있으므로 클라이언트는 다양한 엔드포인트를 요청해야 함 + +```json +{ + "books": [ + { + // 필요한 정보 + "id": 1, + "title": "Do it! 자바 프로그래밍 입문", + "author": "박은종", + // 필요하지 않은 정보 + "price": 25000, + "publisher": " 이지스퍼블리싱", + "isbn": "9791163030195" + }, + { + // 필요한 정보 + "id": 2, + "title": "모두의 한국어 텍스트 분석 with 파이썬", + "author": "박조은, 송영숙", + // 필요하지 않은 정보 + "price": 27000, + "publisher": "길벗", + "isbn": "9791140704521" + } + ] +} +``` +위 문제들이 와닿으셨을까요? GraphQL API를 사용하면 REST API의 단점 중 하나인 Over-Fetching이나 Under-Fetching 문제를 효과적으로 해결할 수 있다고 합니다. + +### 주요 특징 +GraphQL API의 주요 특징은 다음과 같습니다. +1. **유연하고 강력한 데이터 쿼리 언어** + - 클라이언트가 필요한 데이터의 구조와 양을 정확하게 명시할 수 있는 강력한 쿼리 언어를 제공하므로 과도한 데이터 전송이나 다수의 요청을 최소화할 수 있음 + - 즉, REST API의 오버 페칭과 언더 페칭과 같은 이슈가 발생하지 않음 +2. **단일 엔드포인트** + - REST API에서는 각 엔드포인트마다 데이터를 요청해야 했지만, GraphQL은 단일 엔드포인트를 사용하여 클라이언트가 단일 요청으로 여러 데이터를 가져올 수 있음 + - ex) @PostMapping("/graphql") +3. **실시간 데이터 업데이트** + - 실시간 데이터 업데이트를 지원 + - 일반적으로 Subscription이라 불리는 메커니즘을 통해 이루어짐 + +```text +query { + user(id: 123) { + id + name + email + posts { + title + content + } + } +} +``` +```json +{ + "data": { + "user": { + "id": 123, + "name": "John Doe", + "email": "john.doe@example.com", + "posts": [ + { + "title": "GraphQL Basics", + "content": "Introduction to GraphQL" + }, + { + "title": "Advanced GraphQL", + "content": "Deep dive into GraphQL concepts" + } + ] + } + } +} +``` + +## 4. WebSocket API +마지막으로 **WebSocket API**입니다. 일반적인 HTTP 통신은 클라이언트-서버간 요청-응답을 주고 받는 단방향 통신인데요. 채팅, 주식, 온라인 게임과 같은 실시간 애플리케이션에서는 ***빠른 속도로 정보를 전달하고 업데이트*** 하기 위해 양방향 통신이 필요합니다. WebSocket API는 이런 필요성이 요구될 때 사용되는 API랍니다. + +### 주요 특징 +WebSocket API의 주요 특징은 다음과 같습니다. +1. **양방향 통신** + - 클라이언트-서버간 양방향 통신 가능 + - 서버가 클라이언트에게 데이터를 푸시하고, 클라이언트가 서버에게 데이터를 전송할 수 있음 +2. **실시간성** + - 연결을 유지하면서 데이터를 실시간으로 전송 + - 단방향 통신과는 달리, 데이터의 지연 시간을 최소화하여 실시간 응용 프로그램을 구축하는 데 적합 +3. **단일 연결 유지** + - WebSocket은 한 번의 연결을 설정하고 유지함으로써 여러 요청에 대한 새로운 연결을 맺지 않아도 됨 +4. **효율적인 데이터 전송** + - 연결을 유지하면서 계속 데이터를 주고받기 때문에, 새로운 연결을 설정할 필요가 없어 헤더의 오버헤드가 감소함 +5. **이벤트 기반 모델** + - 이벤트 기반의 모델을 사용하여 메시지를 수신하고 처리할 수 있음 + +## 5. API 비교 +앞서 설명했던 내용에 덧붙여 간략하게 API별 비교를 위해 표로 정리해보겠습니다. + + 특징 | REST API | GraphQL API | WebSocket API +----------|--------------------------------|-------------------------------|----------------------------------- + 용도 | 주로 데이터 조회 및 간단한 상호 작용 (CRUD 작업) | 복잡한 데이터 요청 및 실시간 업데이트 | 실시간 데이터 전송 및 양방향 통신 + 사용사례 | 소셜 미디어, 블로그 등 | 복잡한 데이터 요청이 필요한 애플리케이션 | 실시간 채팅, 주식 시세 업데이트, 실시간 협업 등 + 통신방식 | 단방향 요청-응답 구조 | 클라이언트가 필요한 데이터를 정의하고 서버가 응답 | 양방향 통신으로 실시간 데이터 전송 + 지연시간 | 새로운 연결마다 지연 시간이 발생 | 클라이언트가 필요한 데이터만을 요청하여 효율적인 전송 | 연결 유지로 낮은 지연 시간 + 실시간 업데이트 | Polling 또는 Webhooks을 통한 업데이트 | 실시간 데이터 업데이트 | 실시간 데이터 업데이트 + 유연성 | 여러 리소스에 대한 각각의 고유한 엔드포인트를 사용 | 클라이언트가 필요한 데이터만 요청 가능 | 단일 연결로 다양한 메시지 처리 | + 복잡한 쿼리 | 여러 엔드포인트에 각각의 요청을 보내 복잡한 쿼리 처리 | 복잡한 쿼리 및 중첩된 필드 지원 | 주로 간단한 메시지 전송에 사용되며, 덜 복잡한 데이터 구조 + 상태 | 상태 저장이 필요하지 않음 | 상태 저장이 필요하지 않음 | 상태 저장이 필요한 경우 (예: 게임 상태) + 오버헤드 | HTTP 헤더, 상태 코드 등의 오버헤드 발생 | 필요한 데이터만 요청하므로 상대적으로 적음 | 일반적으로 상대적으로 낮은 오버헤드 + +## 6. 정리 +지금까지 기술 세미나에서 나눴던 얘기를 마무리했습니다.
+아래에는 각각의 웹 API를 어떤 상황에서 사용하면 좋을지 간단히 작성해봤습니다. 하나의 프로젝트에서 여러 API를 함께 사용할 수도 있기 때문에 상황에 알맞은 API를 적용해 사용하면 되겠죠~? + +- 주로 상태를 저장하지 않고 단순한 데이터 전송에 사용되며, 서버의 부하가 크게 발생하지 않는 경우 + - REST API +- 클라이언트와 서버 간의 상태를 유지하고 실시간 양방향 통신이 필요한 경우 + - WebSocket API +- 복잡한 데이터 요청이 필요한 경우이거나 실시간 업데이트가 필요한 경우 + - GraphQL API + +정말 정말 마지막으로 영상 하나를 추천하려고 해요!
+이번 세미나를 준비하며 보게 된 영상으로 흥미롭게 들었던 내용이라 공유드립니다. REST API와 관련된 내용인데 자주 사용하는 API인 만큼 시간되실 때 참고해보면 좋을 것 같습니다.
+ +[그런 REST API로 괜찮은가](https://youtu.be/RP_f5dMoHFc?si=9clhxJ_3Ucn5L4U3) + +## 7. 참고자료 +- [API란? 비개발자가 알기 쉽게 설명해드립니다!](https://blog.wishket.com/api란-쉽게-설명-그린클라이언트) +- [RESTful](https://positiveko-til.vercel.app/til/cs/restful.html) +- [REST란? REST API 와 RESTful API의 차이점](https://dev-coco.tistory.com/97) +- [WebSocket API의 기본 구성요소 및 기능](https://appmaster.io/ko/blog/websocket-api-guseong-yoso-mic-gineung) +- [GraphQL](https://graphql.org/learn/) +- [REST API에서 GraphQL로의 패러다임 전환 - Facebook이 주목한 기술](https://enjoydev.life/blog/frontend/11-graphql) +- [실시간으로 최신 데이터를 불러오는 Websocket API, REST API와 어떤 차이가 있을까?](https://youtu.be/LddPLO4bXmQ) +- [[10분 테코톡] 정의 REST API](https://youtu.be/Nxi8Ur89Akw?si=koW8ZxGvhG1xAiRl) +- [[10분 테코톡] ✨ 아론의 웹소켓&스프링](https://youtu.be/rvss-_t6gzg?si=sf4NiNiHzhwAl52D) diff --git a/_posts/2023-11-27-database-normalization.md b/_posts/2023-11-27-database-normalization.md new file mode 100644 index 0000000..c7952f1 --- /dev/null +++ b/_posts/2023-11-27-database-normalization.md @@ -0,0 +1,220 @@ +--- +layout: post +title: 데이터베이스 정규화 초급 +author: 신종민 +categories: 기술세미나 +banner: + image: https://github.com/Kernel360/blog-image/blob/main/2023/1127/Cover.jpeg?raw=true + background: "#000" + height: "100vh" + min_height: "38vh" + heading_style: "font-size: 4.25em; font-weight: bold; text-decoration: underline" +tags: [데이터베이스, 정규화, database 정규화, database normalization] +--- + + +# 10분 기술세미나 + +주제: 데이터베이스 정규화 + +안녕하세요. 커널360 백엔드 1기 4셀 신종민입니다.
+ +제가 이번에 준비한 발표 주제는 데이터베이스의 정규화에 관한 내용입니다.
+부트업과 해커톤, 그리고 End to End 프로젝트를 거치면서 데이터베이스에 대해서 모르는 점이 많다는 점을 깨달았습니다.
파이널 프로젝트 외에도 앞으로도 백엔드 개발자로서 반드시 알아야 한다는 점을 깨닫고 데이터베이스에 대해 잘 알고싶어 발표 주제를 정하게 되었습니다. + +--- +## 1. 정규화의 정의 +정규화란 무엇일까요? 위키피디아에서는 다음과 같이 나왔습니다. + +> 관게형 데이터베이스의 설계에서 중복을 최소화하게 데이터를 구조화하는 프로세스 + +> 크고, 제대로 조직되지 않은 테이블들과 관계들을 작고 잘 조직된 테이블과 관계들로 나누는 것 + +> Database normalization is a database schema design technique, by which an existing schema is modified to minimize redundancy and dependency of data.(데이터베이스 정규화를 통해 데이터의 중복과 의존성을 최소화할 수 있다.) + +이라고 합니다. + +정규화는 데이터베이스 설계에서 중요한 개념 중 하나로, 중복을 최소화하고 데이터를 구조화하는 프로세스입니다. 이를 통해 데이터베이스의 효율성을 높이고 무결성을 유지할 수 있습니다. + +--- +## 2. 정규화의 목적 +데이터베이스 정규화의 구체적인 목적은 다음과 같습니다. +- 데이터베이스의 변경 시 `이상현상` 제거 +- 데이터베이스 구조 확장 시 재 디자인 최소화 +- 사용자에게 데이터 모델을 더욱 의미있게 만듦 +- 다양한 질의 지원 +- 주된 목적은 중복을 최소화하고 데이터의 무결성을 유지하는 것. + +여기서 주요한 개념을 짚고 넘어가야 할 것 같습니다. +첫 번째로 이상현상입니다. +### 이상현상이란? +테이블의 수정시 발생하는 원치 않는 부작용을 이상현상이라고 한다. +이상현상에는 갱신이상, 삽입 이상, 삭제 이상이 있다. + +1. 삽입이상: 자료를 삽입할 때 의도하지 않은 자료까지 삽입해야만 테이블에 추가가 가능한 현상을 말한다. +2. 갱신이상: 중복된 데이터 중 일부만 수정되어 데이터 모순이 일어나는 현상 +3. 삭제이상: 어떤 정보를 삭제하면, 의도하지 않은 다른 정보까지 삭제되어버리는 현상 + +--- +## 3. 정규화의 단계 +정규화의 단계는 제1정규화, 제2정규화, 제3정규화, BCNF정규화, 제4정규화, 제5정규화로 총 6단계가 있으며 실무에서는 보통 제3정규화나 BCNF 정규까지 진행한다고 합니다. + +정규화는 단계적으로 이루어지며 각 단계는 특정한 종속성을 해결하고 데이터의 일관성을 향상시킵니다. + +### 제1정규화 +제1정규화란 테이블의 컬럼이 원자값을 갖도록 하는 것입니다. +우리가 일 대 다 연관관계를 설정할 때, 객체는 일 쪽이 다 쪽의 객체를 포함하는 반면에, +관계 테이블은 다 대 일 관계에서 일의 키를 '다'쪽이 foreign 키로 가지고 있는 것과 관련이되어 있습니다. + +제1정규화를 달성하지 않으면 어떤 문제가 있을까요? 즉 하나의 컬럼이 원자화되지 않거나, 동일한 값을 가지는 컬럼이 여러 가지가 나타나게 된다면 어떤 문제가 생길까요? + + +제1정규화를 지키지 않으면 데이터베이스에서 어떤 문제가 발생할 수 있는지를 예시를 통해 자세하게 설명하겠습니다. + +제1정규화의 주요 목표는 각 컬럼이 원자적인 값을 가져야 한다는 것입니다. 즉, 각 컬럼의 값은 더 이상 분해되거나 중첩되지 않아야 합니다. 제1정규화를 위반하면 다음과 같은 문제가 발생할 수 있습니다. + +### 예시 + +고객 정보를 저장하는 테이블이 있다고 가정해봅시다. + +| 고객ID | 이름 | 전화번호 | +|--------|----------|--------------------| +| 1 | 홍길동 | 010-1234-5678 | +| 2 | 김영희 | 02-9876-5432 | +| 3 | 이철수 | 010-1111-2222 | + +이 테이블은 초기에는 제1정규화를 따르고 있습니다. 그러나 만약 전화번호 컬럼에 여러 전화번호를 저장하려고 한다면, 제1정규화를 위반하게 됩니다. + +| 고객ID | 이름 | 전화번호 | +|--------|----------|--------------------| +| 1 | 홍길동 | 010-1234-5678 | +| 2 | 김영희 | 02-9876-5432 | +| 3 | 이철수 | 010-1111-2222, 02-3333-4444 | + +위의 예시에서는 고객ID 3의 레코드에서 전화번호가 두 개 이상이면서 컬럼에 여러 전화번호가 중첩되어 저장되어 있습니다. 이는 제1정규화를 위반한 상태입니다. + +### 문제점 + +1. **데이터 중복성**: 전화번호 정보가 중첩되어 저장되면서 데이터 중복이 발생합니다. 이로 인해 `데이터 일관성`이 깨질 수 있습니다. + +2. **검색 및 정렬 어려움**: 특정 전화번호를 검색하거나 정렬하는 것이 어려워집니다. 또한 이러한 중첩된 데이터를 처리하는 `쿼리 작성이 복잡`해집니다. + +3. **확장성 문제**: 새로운 전화번호를 추가하거나 기존 전화번호를 수정할 때 일관성을 유지하기 어려워집니다. + +이처럼 제1정규화를 위반하면 데이터의 일관성과 유지보수가 어려워지는 문제가 발생할 수 있습니다. 따라서 각 컬럼은 원자적인 값을 가져야 하며, 필요한 경우 별도의 테이블을 생성하여 관계를 맺어주는 것이 좋습니다. + +--- +### 제2정규화 +제2정규화를 쉽게 설명하자면, 현재 테이블의 주제와 관련이 없는 컬럼을 별도의 테이블로 분리하는 작업을 말합니다. +제2정규화는 기본 키가 `복합키`로 되어있는 경우, 한 필드가 기본키를 이루는 일부 필드에 종속되는 현상이 있을 때 `부분 종속성`이 있다고 하는데, 이 부분 종속성을 제거하는 과정을 제2정규화라고 합니다. +> 따라서 부분 함수 종속성은 기본키가 `복합키`로 되어 있을 경우 발생합니다. + +> 완전 함수 종속성: 모든 속성이 기본 키에 종속됐을 경우 완전 함수 종속성이 있다고 합니다. + +제2정규화를 이해하기 위해, 우선 제2정규화가 지켜지지 않은 상황에서 발생할 수 있는 문제를 예시를 통해 살펴보겠습니다. + +### 예시 + +고객과 주문 정보를 저장하는 테이블이 있다고 가정해봅시다. + +**고객 테이블 (Customer Table):** + +| 고객ID | 이름 | 주소 | +|--------|----------|---------------------| +| 1 | 홍길동 | 서울 강남구 | +| 2 | 김영희 | 부산 해운대구 | +| 3 | 이철수 | 인천 남동구 | + +**주문 테이블 (Order Table):** + +| 고객ID | 주문일자 | 상품 | 가격 | +|--------|------------|-------------|--------| +| 1 | 2022-01-01 | 노트북 | 1,500,000| +| 2 | 2022-01-02 | 스마트폰 | 800,000| +| 1 | 2022-01-03 | 테블릿 | 600,000| +| 3 | 2022-01-04 | 노트북 | 1,000,000| + +여기서 주문 테이블은 고객 테이블과 관련이 있습니다. 그러나 주문 테이블에서는 각 주문의 상품과 가격 정보가 함께 저장되어 있습니다. + +### 문제점 + +1. **부분 종속성**: 위의 주문 테이블은 (고객ID, 주문일자, 상품)이 복합키를 이루는데요, 상품의 가격은 기본키가 아닌 일부 컬럼인 상품에만 종속되어 있습니다. + +2. **데이터 중복성**: 같은 상품을 여러 번 주문 했을 경우 같은 상품에 대한 가격이 곳곳에 흩어져 중복되어 저장되어 있습니다. + +### 제2정규화의 적용 + +제2정규화는 부분 종속성을 해결하기 위한 것입니다. 주문 테이블을 정규화하여 두 개의 테이블로 나누어 보겠습니다. + +**고객 테이블 (Customer Table):** + +| 고객ID | 이름 | 주소 | +|--------|----------|---------------------| +| 1 | 홍길동 | 서울 강남구 | +| 2 | 김영희 | 부산 해운대구 | +| 3 | 이철수 | 인천 남동구 | + +**주문 테이블 (Order Table):** + +| 고객ID | 주문일자(년-월-일))| 상품 ID| +|--------|------------|--------| +| 1 | 2022-01-01 | 1001 | +| 2 | 2022-01-02 | 1002 | +| 1 | 2022-01-03 | 1003 | +| 3 | 2022-01-04 | 1001 | + +**상품 테이블 (Product Table):** + +| 상품ID | 상품 | 가격(원) | +|--------|-------------|--------| +| 1001 | 노트북 | 1,500,000 | +| 1002 | 스마트폰 | 800,000 | +| 1003 | 테블릿 | 600,000 | +| 1004 | 데스크탑 | 2,000,000 | + +이렇게 분리된 테이블은 각 테이블이 각자의 의미를 가지며, 부분 종속성 문제가 해결됩니다. 고객 테이블은 주문 테이블에 대해 부분 종속성이 없어지고, 데이터 중복성이 줄어들어 더 효율적인 데이터 관리가 가능해집니다. + + +--- +### 제3정규화 +제3정규화는 `이행함수 종속성`을 제거하는 과정을 말합니다. 이행하수 종속성이란 어느 컬럼이 기본 키가 아닌 다른 컬럼에 정속되는 현상을 말합니다. + + +자세한 예시를 통해 이행함수 종속성을 이해해보겠습니다. + +고려할 테이블은 학생과 학과에 대한 정보를 담고 있는 테이블입니다. + +**학생 테이블 (Students Table):** + +| 학번 | 이름 | 전공 | +|--------|--------|--------| +| 1001 | 홍길동 | 컴퓨터과학 | +| 1002 | 김영희 | 경영학 | +| 1003 | 이철수 | 전자공학 | + +여기서 학번은 이름을 결정하고, 이름은 전공을 결정합니다. 이 경우, 학번 → 이름, 이름 → 전공이 성립합니다. + +이제 이를 정규화하기 위해 학번과 이름을 가지는 테이블과 이름과 전공을 가지는 테이블로 나누어 보겠습니다. + +**학생 테이블 (Students Table):** + +| 학번 | 이름 | +|--------|--------| +| 1001 | 홍길동 | +| 1002 | 김영희 | +| 1003 | 이철수 | + +**전공 테이블 (Major Table):** + +| 이름 | 전공 | +|--------|--------| +| 홍길동 | 컴퓨터과학 | +| 김영희 | 경영학 | +| 이철수 | 전자공학 | + +이제 학번과 이름이 하나의 테이블에서 다른 테이블로 분리되었습니다. 이를 통해 학번이 이름에, 이름이 전공에 이행함수 종속되던 문제를 해결할 수 있습니다. 이렇게 데이터 중복성을 최소화하고 테이블 간의 의미 있는 관계를 유지하며 정규화가 이뤄진 것입니다. + +--- +## 마무리 +적절한 수준의 데이터베이스 정규화로 데이터를 중복되지 않고 이상현상이 발생하지 않도록 할 수 있습니다. +하지만 정규화를 하면 join 연산의 필요성도 늘어나서 비정규화 과정을 거치는 사례도 있는만큼 상황에 맞는 적절한 전략이 필요합니다. \ No newline at end of file diff --git a/_posts/2023-11-28-N+1.md b/_posts/2023-11-28-N+1.md new file mode 100644 index 0000000..6da9a5d --- /dev/null +++ b/_posts/2023-11-28-N+1.md @@ -0,0 +1,275 @@ +--- +layout: post +title: N+1 +author: 홍주광 +categories: 기술세미나 +banner: + image: https://raw.githubusercontent.com/Kernel360/blog-image/main/2023/1128/4_N+1문제란.png + background: "#000" + height: "100vh" + min_height: "38vh" + heading_style: "font-size: 4.25em; font-weight: bold; text-decoration: underline" +tags: [Java, ORM, JPA, N+1, SQL, 최적화] +--- + +안녕하세요. N+1 주제로 발표하게 된 홍주광입니다. + +*사례* 먼저 보시겠습니다. + +![1](https://raw.githubusercontent.com/Kernel360/blog-image/main/2023/1128/1_사례배포전.png) + +클라이언트에서 서버로 초당 5회의 요청을 보냈고 평균 30ms속도로 응답을 받았습니다. + +조금 더 올려볼까요? + +![2](https://raw.githubusercontent.com/Kernel360/blog-image/main/2023/1128/2_사례배포전2.png) + +초당 1000회의 요청을 보내봤습니다. 어떻게 되었을까요? + +![3](https://raw.githubusercontent.com/Kernel360/blog-image/main/2023/1128/3_사례배포전3.png) + +초당 1000회의 요청을 보냈고 평균 4000ms속도의 응답을 받았습니다. 서버는 과부하가 되었습니다. + +왜 이런 현상이 벌어졌을까요? + +바로 이 사례에서는 `N+1` 문제 때문이였습니다. (물론 항상 위 현상이 N+1 문제로 일어나는 것은 아닙니다..) + +그만큼 N+1 문제가 서버에 영향을 줄 수 있다는 뜻입니다. + +## N+1 + +N+1 문제란 무엇일까요? + +![4](https://raw.githubusercontent.com/Kernel360/blog-image/main/2023/1128/4_N+1문제란.png) + +데이터 조회 시, 1개의 쿼리로 요청이 처리될 것이라고 생각했는데 N개의 추가 쿼리가 더 발생하는 문제입니다. + +## N+1 문제 예시 + +N+1 문제의 정의만 듣고는 이해가 어려울 수도 있어 예시를 준비해보았습니다. + +![5](https://raw.githubusercontent.com/Kernel360/blog-image/main/2023/1128/5_N+1사례.png) + +주인와 고양이가 1:N 인 일대다 관계로 있습니다. + +한 명의 주인이 여러 고양이를 키울 수 있습니다. + +주인은 10명이고 각 주인이 10마리의 고양이를 키운다고 가정하고 + +모든 주인을 조회해보겠습니다. + +우리는 모든 주인을 조회하는 쿼리이니 당연히 1개의 쿼리가 나올 것이라고 생각할 것입니다. + +![6](https://raw.githubusercontent.com/Kernel360/blog-image/main/2023/1128/6_N+1사례_쿼리.png) + +그러나 1개의 쿼리가 아닌 총 11개의 쿼리가 더 나왔습니다. + +여기서 조금 더 자세히 짚고 넘어가자면 + +빨간 박스인 예상 쿼리를 보시면 모든 주인을 조회하는 쿼리가 나왔습니다. 10명의 주인이 조회되었습니다. + +노란 박스인 추가 쿼리를 보시면 10개가 더 나왔는데 이것은 고양이의 숫자와 상관없이 10명의 주인이 각 1개의 쿼리가 더 생긴 것입니다. + +즉, + +* 1번주인, 2번주인, 3번주인, 4번주인, 5번주인, 6번주인, 7번주인, 8번주인, 9번주인, 10번주인 + +이 조회 되었고 + +* 1번주인의 고양이를 조회하는 쿼리, 2번주인의 고양이를 조회하는 쿼리, 3번주인의 고양이를 조회하는 쿼리, 4번주인의 고양이를 조회하는 쿼리, 5번주인의 고양이를 조회하는 쿼리, +6번주인의 고양이를 조회하는 쿼리, 7번주인의 고양이를 조회하는 쿼리, 8번주인의 고양이를 조회하는 쿼리, 9번주인의 고양이를 조회하는 쿼리, 10번주인의 고양이를 조회하는 쿼리 + +이렇게 각 주인마다 1개의 쿼리가 추가로 나와 총 10개의 쿼리가 추가로 나오게 된 것입니다.(고양이 수는 중요하지 않음!) + +다른 예시로 + +주인이 5명이고 각 주인이 고양이를 2마리씩 키우고 있다고 하면 + +N+1문제가 발생했을 때는 1(모든 주인 조회 쿼리) + 5(각 주인의 고양이 조회 쿼리) 해서 총 6개의 쿼리가 나오게 됩니다. + +## N+1 발생 이유 + +JPQL 이 처음 쿼리를 만들 때, 연관관계가 있는 엔티티는 신경 쓰지 않고 조회 대상이 되는 엔티티 기준으로만 쿼리를 만들기 때문에 발생합니다. + +이는 JPA 뿐만 아니라 ORM 은 다 발생하는 문제입니다. + +## 글로벌 페치 전략 + +### 즉시로딩 + +즉시로딩(EAGER) 은 부모 엔티티를 조회할 때 연관된 자식 엔티티를 함께 가져오는 방식 + +### 지연로딩 + +지연로딩(LAZY) 은 부모 엔티티를 조회할 때 연관된 자식 엔티티를 필요한 시점까지 로딩하지 않고 지연하여 가져오는 방식 + +## 페치 전략으로 해결 가능할까? + +구글링을 해보면 많은 사람들이 페치 전략을 통해 N+1 문제를 해결하려는 시도를 볼 수 있습니다. + +### 즉시 로딩 페치 전략 + +> 즉시 로딩으로 한번에 불러오면 되는 거 아닌가요? + +``` +@OneToMany(mappedBy = "owner", fetch = FetchType.EAGER) +private List cats = new ArrayList<>(); +``` + +모든 주인을 조회해보겠습니다. + +![7](https://raw.githubusercontent.com/Kernel360/blog-image/main/2023/1128/7_즉시로딩.png) + +여전히 N+1문제가 발생합니다. + +1. JPQL에서 만든 SQL을 통해 데이터를 조회 +2. 이후 JPA에서 Fetch 전략을 가지고 해당 데이터의 연관 관계인 하위 엔티티들을 추가 조회 +3. 2번 과정으로 N+1 문제 발생 + +**즉시로딩으로는 N+1 문제를 해결할 수 없습니다.** + +### 지연 로딩 페치 전략 + +> 지연 로딩으로 하면 한 줄만 나오던데요? + +``` +@OneToMany(mappedBy = "owner", fetch = FetchType.LAZY) +private List cats = new ArrayList<>(); +``` + +모든 주인을 조회해보겠습니다. + +쿼리가 진짜 하나만 나옵니다. 그러나 고양이 이름도 같이 조회하게 되면 + +![8](https://raw.githubusercontent.com/Kernel360/blog-image/main/2023/1128/8_지연로딩.png) + +여전히 N+1 문제가 발생합니다. + +연관된 데이터를 찾지 않는 경우(고양이 이름을 검색하지 않을 때)에는 한 줄로 나옵니다. +-> 그래서 기본적으로 연관관계는 지연로딩(LAZY)가 좋습니다. + +1. JPQL에서 만든 SQL을 통해 데이터를 조회 +2. JPA에서 Fetch 전략을 가지지만, 지연 로딩이기 때문에 추가 조회는 하지 않음 +3. 하지만, 하위 엔티티를 가지고 작업하게 되면 추가 조회가 발생하기 때문에 결국 N+1 문제 발생 + +**임시적으로 나오지 않게 할 수는 있지만 근본적으로 지연로딩도 N+1 문제를 해결할 수 없습니다.** + +둘의 차이는 언제 쿼리를 발생시키냐의 차이입니다. + +## N+1 문제 해결 방법 + +대표적인 해결 방법으로는 3가지가 있습니다. + +* 페치 조인 + +* 엔티티 그래프 + +* 배치 사이즈 + +그 중 페치 조인과 배치 사이즈를 알아보겠습니다. + +### 페치 조인 + +* JPQL에서 성능 최적화를 위해 제공되는 기능 + +* JPQL을 사용하여 DB에서 데이터를 가져올 때 처음부터 연관된 데이터까지 같이 가져오게 하는 방법 + +``` +@Query("select o from Owner join fetch o.cats") +List findAllJoinFetch(); +``` + +![9](https://raw.githubusercontent.com/Kernel360/blog-image/main/2023/1128/9_페치조인.png) + +1개의 쿼리가 나오며 `inner join` 이 사용됩니다. + +### 페치 조인 주의점 + +* JPA 가 제공하는 Paging사용 불가능(OneToMany,ManyToMany 관계) + +* 1:N 관계가 두 개 이상인 경우 사용 불가(OneToMany,ManyToMany 관계) + +* 페치 조인 대상에게 별칭 부여 불가 + +* 중복 데이터 발생 가능성 + +### 배치 사이즈 + +* 하이버네이트가 제공하는 @BatchSize 어노테이션을 이용 + +* 연관된 엔티티를 조회할 때 지정된 size 만큼 SQL의 IN절을 사용해서 조회 + +* 완전한 N+1문제 해결은 아님 + +* 1000번 일어날 N+1 문제를 1번만 더 조회하는 방식으로 성능을 최적화(size=1000) + +``` +select * from user where team_id in (?,?,?) +``` + +배치사이즈를 사용하게 되면 위처럼 in 절로 나오게 됩니다. + +배치사이즈는 무조건 1개의 쿼리가 나오는 것은 아닙니다. + +배치 사이즈의 크기는 프로젝트마다 최적의 사이즈가 다르지만 보통 1000이 MAX 로 둔다고 알려져 있습니다. + +이 부분이 궁금하시면 찾아보시길 권장드립니다. + +![10](https://raw.githubusercontent.com/Kernel360/blog-image/main/2023/1128/10_배치사이즈1.png) + +yml 파일에서 위처럼 설정할 수도 있고 + +![11](https://raw.githubusercontent.com/Kernel360/blog-image/main/2023/1128/11_배치사이즈2.png) + +엔티티에서 바로 설정할 수도 있습니다. + +### 배치사이즈 예시 + +조금 더 이해를 돕기 위해 예시를 들어 설명해보겠습니다. + +``` +@BatchSize(size=5) +@OneToMany(mappedBy = "owner", fetch = FetchType.EAGER) +``` + +![12](https://raw.githubusercontent.com/Kernel360/blog-image/main/2023/1128/12_배치사이즈3.png) + +주인이 10명 고양이가 10명은 일대다 관계에서 사이즈가 5이면 + +처음 조회쿼리 1개 + 배치사이즈로 인한 in절 쿼리(size5) 2개 = 총 3개의 쿼리가 나오게 됩니다. + +``` +@BatchSize(size=20) +@OneToMany(mappedBy = "owner", fetch = FetchType.EAGER) +``` + +사이즈를 20으로 늘리면 어떻게 될까요? + +![13](https://raw.githubusercontent.com/Kernel360/blog-image/main/2023/1128/13_배치사이즈4.png) + +in절 쿼리가 20개까지 커버 가능하니 + +처음 조회쿼리 1개 + 배치사이즈로 인한 in절 쿼리(size20) 1개 = 총 2개의 쿼리가 나오게 됩니다. + +## 정리 + +1. N+1 문제는 데이터 조회 시, 1개의 쿼리로 요청이 처리될 것이라고 생각했는데 N개의 추가 쿼리가 더 발생하는 문제이다. +2. N+1 문제의 발생 원인은 JPQL 이 처음 쿼리를 만들 때 연관관계가 있는 엔티티는 신경 안쓰고 조회 대상이 되는 엔티티 기준으로만 쿼리를 만들기 때문이다. +3. N+1 문제는 글로벌 페치 전략으로 해결할 수 없다. +4. 페치 조인은 JPQL을 사용하여 DB에서 데이터를 가져올 때 처음부터 연관된 데이터까지 같이 가져오게 하는 방법이다. +5. 배치사이즈는 연관된 엔티티를 조회할 때 지정된 size 만큼 SQL의 IN절을 사용해서 조회하는 방법이다. + +N+1 문제는 면접에서 대답을 못하면 절대 안되는 문제로 꼭 숙지하고 가시길 바랍니다. +또한 N+1 문제를 고려하면서 연관관계와 쿼리를 작성하고 본인에게 맞는 방법으로 해결하시길 바랍니다. + +## 출저 + + + + + + + + + + diff --git a/_posts/2023-12-01-RDBMS.md b/_posts/2023-12-01-RDBMS.md new file mode 100644 index 0000000..ec57a35 --- /dev/null +++ b/_posts/2023-12-01-RDBMS.md @@ -0,0 +1,208 @@ +--- +layout: post +title: RDBMS의 조회과정, 쿼리실행계획 중요성 +author: 정지용 +categories: 기술세미나 +banner: + image: https://github.com/Kernel360/blog-image/blob/main/2023/1201/header.png?raw=true + + background: "#000" + height: "100vh" + min_height: "38vh" + heading_style: "font-size: 4.25em; font-weight: bold; text-decoration: underline" + tags: [RDBMS, 오라클, MYSQL, DML, QUERYPLAN, 기술세미나] +--- + + +# RDBMS의 조회과정, 쿼리실행계획 중요성 + +반갑습니다. +RDBMS와 쿼리실행계획의 중요성을 주제로 기술세미나 발표를 한 정지용 입니다. + +## [1. 개요] + +요즘 자바와 ORM을 기반으로 개발을 하게되어 직접적으로 쿼리를 보는 일이 예전보다 현저히 줄어들었다고는 하나 아직도 데이터가 저장되는 저장소는 대부분 데이터베이스입니다. + +결국 그 말은 즉슨 아무리 데이터베이스에 의존을 최소화 하더라도 어쩔 수 없이 데이터베이스에 대해 잘 알아야 한다는 이야기이기도 합니다. + +이번 시간에는 RDBMS에서 다양한 기능을 많이 지원하지만 가장 많이 쓰이는 조회과정과 이와 연관되어있는 쿼리실행계획의 중요성을 알아보는 시간을 가져보고자 합니다. + +
+ +## [2. RDBMS란?] + +진행하기 앞서, 데이터베이스(Database, 이하 DB)란 무엇일까요? + +짧고 명료하게 이야기 하자면 집합과 명제를 근간으로 하는 체계적인 데이터 모음입니다. + +RDBMS (관계형 데이터베이스 관리 시스템 :: Relational Database Management System)는 이름에서도 유추가 되듯 데이터베이스의 관리 시스템입니다. + +이 RDBMS에선 보통 일반적으로 4가지의 명령어를 지원합니다. + +![스크린샷 2024-02-14 134256](https://github.com/gunsight1/blog/assets/103917282/11ceddc9-60fb-4723-a7ca-551c4b1b8916) + +DDL(Data Definition Language)은 데이터 정의어로 DB 구조를 정의하거나 변경하는데 쓰입니다. + +DML(Data Manipulation Language)은 데이터 조작어로 등록(insert) 조회(select) 수정(update) 삭제(delete)를 수행합니다. + +DCL(Data Control Language)은 데이터 제어어로 DB에 접근하는 사용자의 권한관리를 담당합니다. + +TCL(Transaction Control Language)은 트랜잭션 제어어로 작업수행의 대한 승인(commit), 철회(rollback)을 담당합니다. + +이 중 우리가 알아볼 명령어는 DML에 속하는 SELECT이며 DBA가 아닌 이상 우리같은 개발자는 보통 DML과 TCL을 세트로 많이 사용합니다. + +DML의 가장 큰 특징은 DDL과 다르게 수행을 한 뒤 데이터가 즉시 DB에 반영되지 않으므로 TCL을 통해 최종 수행여부를 결정 해야합니다. + +
+ +## [3. SELECT의 조회 과정] + +DBMS의 명령어는 직관적인 편이며 SELECT의 문법도 이와 마찬가지입니다. + +SELECT, 선택한다. 무엇을? 컬럼을. + +FROM, 어디로부터? TABLE에서 + +WHERE, 어떤 기준으로? 조건에 따라 + +GROUP BY 어떻게 묶어? 컬럼에 맞춰서 + +HAVING 어떤 기준으로? 조건에 따라 + +ORDER BY 정렬은? 오름차순 / 내림차순 + +![스크린샷 2024-02-14 140524](https://github.com/gunsight1/blog/assets/103917282/0f5afa37-bd98-4b8a-82be-bc114ded1634) + +위의 이미지에서 노란 박스안의 문법들은 옵션을 의미합니다. + +옆의 예제 SQL문법이 한눈에 읽히신다면 여러분들은 SELECT 쿼리를 쓸 줄 아시게 된겁니다. + +문법을 알아보았으니 이제 실행 과정을 살펴볼건데 이해하기 쉽게 뷔페에서 요리 담는 것으로 비유를 해봤습니다. + + +![스크린샷 2024-02-14 135018](https://github.com/gunsight1/blog/assets/103917282/e1886f5e-e50f-40d4-bb41-ac05ec3092bd) + +FROM → 뷔페에 모든 요리들이 잔뜩 있는 상태입니다. + +WHERE → 내가 먹고 싶은 음식을 생각해봅시다. + +GROUP BY → 에피타이저, 메인, 디저트처럼 종류에 따라 나눠봅니다. + +HAVING → 나눈 음식중에 생각해보니 국수랑 튀김은 아니다 싶어 빼기로 했어요. + +SELECT → 이제 먹을 음식을 다 골랐고, 접시에 담았습니다. + +ORDER BY → 이 음식을 순서대로 먹습니다. + +결과로 보면 아래와 같은 이미지처럼 흐르게됩니다. + + +![스크린샷 2024-02-14 135025](https://github.com/gunsight1/blog/assets/103917282/6c386616-902b-4bc5-98b6-f6b26c15b2e8) + +
+ +## [4. SELECT의 활용] + +SELECT는 활용도가 아주 높은 DML이고, 서브쿼리(sub-query)라는 특별한 기능을 제공합니다. + +서브쿼리란 쿼리 안에 또 쓰이는 다른 쿼리를 뜻합니다. + +DML에선 포괄적으로 쓰일 수 있고, DDL과의 혼용도 일부 지원을 합니다. + +대표적으로 자주 쓰이는 응용 3가지를 예시로 설명드리겠습니다. + +![스크린샷 2024-02-14 135158](https://github.com/gunsight1/blog/assets/103917282/b4ccc05d-aa38-43ce-8b59-ffce7fab0183) + +첫째로 백업, CREATE 명령어와 조합하면 테이블 백업으로도 쓸 수 있습니다. + +노란박스의 조건을 보면 1은1로 할 경우 데이터까지 복사 + +1은0으로 하면 테이블 스키마만 복사합니다. + +![스크린샷 2024-02-14 140720](https://github.com/gunsight1/blog/assets/103917282/5f6c0366-7693-4b6c-945d-8207d31af973) + +둘째로 필터링입니다. 예를 들어 INSERT명령어와 조합하면 조건에 맞는 데이터만 뽑아 넣을 수 있습니다. + +![스크린샷 2024-02-14 140725](https://github.com/gunsight1/blog/assets/103917282/47927533-50e8-4a53-95c3-0aec17287883) + +셋째로 SELECT안에 SELECT를 써서 임시 테이블, 즉 버추얼 뷰처럼 쓸 수 있는데 이것을 인라인 뷰라고 합니다. + +![스크린샷 2024-02-14 135310](https://github.com/gunsight1/blog/assets/103917282/f69c7a35-432f-4940-87ca-9f0bc0ccddf1) + +위 이미지에 보이는 쿼리에서 첫번째는 조인을 이용한 쿼리 실행이고, 두번째는 인라인 뷰를 이용한 쿼리 실행 입니다. + +둘이 같은 결과를 보여주는 쿼리인데 어느 쪽이 더 빠르게 될까요? 쿼리를 다루게 된다면 항상 고민하게되는 포인트입니다. + +지금껏 이야기 한 SELECT 과정을 실제 RDBMS에서 수행되는 과정을 풀어보면 아래와 같은 이미지처럼 나오게 됩니다. + +DBMS에 관심이 있어 자세히 알아가고자 한다면 아래의 내용에 대해서도 공부해봄직합니다. 🫡 + +![스크린샷 2024-02-14 135358](https://github.com/gunsight1/blog/assets/103917282/7fe177f8-3bdd-478f-a397-ec8437884cb3) + +
+ +## [5. 쿼리실행계획] + +우리의 입장에서 이것보다 더 먼저 알아야 할 것은 SELECT 쿼리가 실행 될 때 발생하는 실행계획(query-plan) 이란 것이 있습니다. + +이 실행계획은 말 그대로 쿼리가 실행 할 때 어떠한 계획을 가지고 수행하는가를 나타내는 계획서입니다. 이 실행계획의 각 단계를 COST, 즉 비용이라고 부르는데 말그대로 수행에 필요한 비용을 뜻합니다. + +우리같은 서민은 가성비를 좋아하듯, 쿼리의 성능도 가성비가 중요합니다. 🙃 보통 다른 환경이 동일하다는 가정하에 코스트가 낮을수록 결과를 빠르게 가져옵니다. + +같은 결과를 뽑더라도 이 실행 계획에 따라 성능이 좌지우지되므로 얼마나 중요한지 알려드리고자 아주 극단적이지만 심플한 예시를 통해 말씀드리겠습니다. + +## [6. 실행계획을 직접 확인해보자] + +![스크린샷 2024-02-14 135852](https://github.com/gunsight1/blog/assets/103917282/694cdd8e-cefc-42d7-956c-e53a1c8ec749) + +자 우리가 취업을 하게되서 회사에서 '통계 쿼리를 만들어라' 업무 지시를 받았다고 시나리오를 두고 위와 같은 구조를 가진 회원 정보 테이블이 있다 가정합시다. + +이 데이터를 토대로 전체 회원 수, 월 가입자 수, 6개월 이상 미접속자 수 3개를 가져오고자 합니다. + +![스크린샷 2024-02-14 135903](https://github.com/gunsight1/blog/assets/103917282/dcafd47d-9987-45cc-8719-cfe9f3828dd5) + +쿼리를 써본지 얼마 안된 초보자의 입장에선 방금전 문법을 알아가는 과정을 통해 SELECT를 가지고 세가지의 값을 가져와야 한다는 것은 인지 할 것입니다. + +그런데 각각 성격이 다른 값을 어떻게 하나로 묶어야할지 고민이 될겁니다. + +일단 각각의 결과를 뽑는 쿼리를 써보자면 보시는 그림과 같이 나올 것입니다. + +![스크린샷 2024-02-14 135935](https://github.com/gunsight1/blog/assets/103917282/21cb7b2d-1213-4b85-a59b-4d4725b67399) + +이제 이것을 하나의 결과로 보여주기위해 FROM절엔 테이블이 들어가야 한다는 문법을 생각해서 다음과 같이 작성했다고 가정해봅시다. + +FROM절에 아까 작성한 각각의 결과를 가져오는 쿼리를 넣어주고, SELECT절에서 값을 가져오게 했습니다. 문법상 아주 정확하고, 올바른 결과를 나타냅니다. + +앞으로 이와 같은 두번의 과정이 더 있는데 결과는 모두 동일함을 미리 말씀드립니다. + +![스크린샷 2024-02-14 140005](https://github.com/gunsight1/blog/assets/103917282/28dd9112-c97c-4b46-9c1d-0d4a8642fc64) + +작성한 쿼리 앞에 실행 계획을 확인하는 EXPLAIN을 넣고 다시 쿼리를 실행해보면 다음과 같이 실행 계획이 6개나 잡혀있는 것을 확인 할 수 있습니다. + +FROM절에 나온 서브쿼리 SELECT가 3번 사용되어 같은 테이블에 3번 요청을 하고, SELECT절에서도 3번의 결과를 가져온 꼴이 되어 결과적으로 3 더하기 3 = 6이 나왔습니다. + +회사에서 쿼리를 이렇게 짜고 보고를 하면 어떻게 될까요? 아이고 고생많았으니 얼른 퇴근하세요 ~ 라고 할까요? 안타깝게도 칼퇴는 커녕 야근으로 직행입니다.🫠 + +![스크린샷 2024-02-14 140044](https://github.com/gunsight1/blog/assets/103917282/fbd9ccfd-bf1e-4add-af54-ae805f5dbfe6) + +자, 야근을 피하기 위해 이번엔 SELECT절 처음부터 각각의 값을 가져오도록 바꿔보았습니다. + +위의 코드에서 MYSQL은 문법상 보이진 않지만 FROM절이 있다고 가정을 해서 보는 것이 맞습니다. + +참고로 오라클에선 문법 가독성을 위해 FROM DUAL이라는 것을 지원합니다. + +FROM절을 가상의 테이블로 취급하여 1개, 서브쿼리로 뽑아오는 부분 3개로 아까보다 무려 2개나 줄었으니 이번엔 칼퇴각일 것 같죠?? + +여러분들께서 이 쿼리를 보고 만족한다면 안타깝게도 또 하루 야근의 길로 당첨입니다. + +그럼 여기서 또 어떻게 줄여야할까요? + +![스크린샷 2024-02-14 140119](https://github.com/gunsight1/blog/assets/103917282/1fb16e96-fd5e-4b30-8501-40671fd75f4d) + +DBMS에서 지원하는 SUM 함수와 자바의 IF와 비슷한 CASE, WHEN, THEN 문법을 이용해서 단 한번으로 데이터를 가져오도록 바꿔보았습니다. + +와우 코스트가 단 하나로 바뀌었네요. 이정도면 고생했단 칭찬과 함께 칼퇴 할 만 합니다. + +이렇게해서 같은 결과를 보여도 쿼리를 어떻게 짜느냐에 따라 효율이 올라가는지, 그 효율을 보는 실행계획은 어떻게 보는지 아주 기초적인 내용으로 알려드렸습니다. + + diff --git a/_posts/2023-12-05-how-to-start-clean-code.md b/_posts/2023-12-05-how-to-start-clean-code.md new file mode 100644 index 0000000..4b8177f --- /dev/null +++ b/_posts/2023-12-05-how-to-start-clean-code.md @@ -0,0 +1,114 @@ +--- +layout: post +title: 클린코드, 뭐부터 시작해볼까? +author: 조형준 +categories: 기술세미나 +banner: + image: https://github.com/Kernel360/blog-image/blob/main/2023/1205/1.png?raw=true + background: "#000" + height: "100vh" + min_height: "38vh" + heading_style: "font-size: 4.25em; font-weight: bold; text-decoration: underline" + tags: [클린코드, 기술세미나] +--- +## 개요 +안녕하세요, 커널360의 크루로 활동중인 조형준입니다. +이번 기술세미나의 주제는 '클린 코드'를 선정해봤습니다. +클린코드는 '읽기 쉬운 코드', '협업하기 좋은 코드' 등과 같은 특징을 가지고 있는데요. +본 포스팅에서 어떤것부터 시작하면 좋을 지 찬찬히 따져보도록 하겠습니다. + +## 클린코드? +![2.png](https://github.com/Kernel360/blog-image/blob/main/2023/1205/2.png?raw=true) +개발자 필독도서라고 한번쯤은 들어보셨을 그 책입니다. +책의 이름이기도 하지만, 결과적으로는 좋은코드, 읽기 좋은 코드 등의 의미와 일맥상통한다고 생각합니다. +클린코드에서 말하는 규칙은 여러개가 있는데, 몇가지만 톺아보자면 다음과 같습니다. +- 함수는 하나의 기능만 +- 나쁜 주석 배제 +- 좋은 함수 이름 +- 철저한 네이밍 컨벤션 +- 고치기 쉬운 코드 +- 그 외 등등.. + +## 적용 사례 +![3.png](https://github.com/Kernel360/blog-image/blob/main/2023/1205/3.png?raw=true) +간단하게 예제를 하나 들어보겠습니다. +위 코드는 제가 E2E 프로젝트에서 진행했던 코드인데요, 게시글의 좋아요 여부값을 가져오는 코드입니다. +로직상으로는 크게 문제는 없지만, 어떤 문제가 있을까요? 다음과 같습니다. +- 좋아요 여부값을 가져오는 함수에서 사용자의 이메일을 가져오고있음. +- 함수의 역할에 맞지 않게 유저 정보를 가져오는 작업을 추가로 진행중 +- 자연스럽게 코드의 길이는 늘어나고, 가독성이 안좋아짐. + +위와 같은 문제를 해결하기 위해서, 다음과 같이 코드를 수정해봤습니다. +![4.png](https://github.com/Kernel360/blog-image/blob/main/2023/1205/4.png?raw=true) +게시글 좋아요의 여부를 가져오는 메서드와, 로그인 유저의 id를 가져오는 메서드를 분리했습니다. +이처럼, 클린코드는 거창한게 아닙니다. 책임을 최대한 분리해주고, 가독성 향상을 위한 노력이 클린코드를 만들어줍니다. +하지만 이렇게 수정한 코드도 누군가에겐 dirty code가 될테죠... (주륵) +![5.png](https://github.com/Kernel360/blog-image/blob/main/2023/1205/5.png?raw=true) + +## 그렇다면 우리는 뭘 해야할까? +To-do 도 중요하지만, 더 중요한 것은 Not-To-Do 라고 생각합니다. +하지말라는 것만 안해도, 코드가 저절로 보기 좋아집니다. +따라서, 본 포스팅에서 소개해드리는 안티패턴을 먼저 지양하고, 주니어 레벨에서 할 수 있는 것부터 천천히 실행해봅시다. + +### Arrowhead +![6.png](https://github.com/Kernel360/blog-image/blob/main/2023/1205/6.png?raw=true) +화살촉 패턴이라는 이름의 이 안티패턴은, 사진이 모든걸 설명해줍니다. +과도한 if문으로 if문의 depth가 늘어나는 것을 지양해야 합니다. 아래와 같이 수정할 수 있겠습니다. +![7.png](https://github.com/Kernel360/blog-image/blob/main/2023/1205/7.png?raw=true) + +### Magic numbers +![8.png](https://github.com/Kernel360/blog-image/blob/main/2023/1205/8.png?raw=true) +이 코드만 보고 무슨 로직인지 이해가 가시나요? +이 코드는 유저의 id가 100, 즉 사장이라면 견적서를 수정할 수 있게 하라는 의미입니다. +특정 상수가 특별한 의미를 갖지 말게 해야합니다. 다음처럼 수정할 수 있겠습니다. +![9.png](https://github.com/Kernel360/blog-image/blob/main/2023/1205/9.png?raw=true) + +### Cryptic names +![10.png](https://github.com/Kernel360/blog-image/blob/main/2023/1205/10.png?raw=true) +이 친구도 사진이 모든걸 설명해줍니다. +`i`, `p`가 도대체 뭘까... 사람이 이해할 수 있는 변수나 메서드명을 사용하자는 것입니다. 즉, 암호를 쓰지맙시다. +다음과 같이 수정해봅시다. +![11.png](https://github.com/Kernel360/blog-image/blob/main/2023/1205/11.png?raw=true) + +### Single funtion, Multiple behaviors +![12.png](https://github.com/Kernel360/blog-image/blob/main/2023/1205/12.png?raw=true) +하나의 함수가 여러 역할, 즉 책임을 맡지 말자는 의미입니다. +위 코드는 calculate라는 함수가 사칙연산을 수행하고 있는데요, 다음과 같이 역할을 분리해줍시다. +![13.png](https://github.com/Kernel360/blog-image/blob/main/2023/1205/13.png?raw=true) + +### God class +![14.png](https://github.com/Kernel360/blog-image/blob/main/2023/1205/14.png?raw=true) +하나의 클래스가 너무 많은 역할을 하지 않도록 하는 것입니다. +방금 전 언급한 안티패턴과 일맥상통합니다. + +### Silencing Error/Exceptions +![15.png](https://github.com/Kernel360/blog-image/blob/main/2023/1205/15.png?raw=true) +이 패턴은 실제 개발을 하면서 많이 겪게되는 문제일 것 같은데요. +조용한 에러와 예외를 만들지 말자는 것입니다. 말로는 조금 감이 안잡히실텐데, 위 코드의 문제점이 무엇일까요? +해당 코드는 유저가 작성한 파일을 모두 삭제하고, 유저를 비활성화 시키는 로직입니다. +그러나 모종의 이유로 try문에서 실행이 되지 않더라도, 유저를 비활성화 시키는 로직이 실행되게 됩니다. +한 두명인 경우 큰 문제는 안되겠지만, 대규모 시스템이라면 당연히 문제가 되겠죠? 이런 코드는 아래와 같이 바꿔볼 수 있겠습니다. +try-catch문을 지워서 에러가 터지면 바로 종료되게 하거나, 파일 삭제를 삭제될때까지 계속 돌리는겁니다. +![16.png](https://github.com/Kernel360/blog-image/blob/main/2023/1205/16.png?raw=true) + +### Bad Comment +![17.png](https://github.com/Kernel360/blog-image/blob/main/2023/1205/17.png?raw=true) +> Good code is self-documenting +> 좋은 코드는 그 자체로 이미 훌륭한 주석이다 + +불필요한 주석이나, 너무 긴 주석은 자제하는 것이 좋습니다. +다만, 도메인 지식이나 구성원들이 알면 좋은 정보들은 주석으로 남겨주면 좋습니다. + +## 마치며 +이렇게 클린코드를 뭐부터 시작해야 할지, 안티패턴들을 학습해보는 시간을 가졌습니다. +간단하게 정리를 해보겠습니다. +- 클린코드 법칙을 실천하기 앞서, 안티 패턴 코드를 지양하자. +- 클린코드를 만드는 방법보다 좀 더 쉬울 것. +- 안티 패턴 코드를 지양하면서, 클린코드 법칙을 천천히 적용하자. +- 뭐가 됐건, 스스로 가독성이 좋은 코드라고 느껴질 때까지 리팩토링해보자. + +여러분들도 코드를 작성하시다가, 해당 포스트의 내용을 떠올리면서 코드를 작성한다면, 클린코드에 한층 더 가까워질 수 있을 것이라 생각합니다! + +**참고영상** +[코드에 이런 짓만 안 해도 욕먹지 않는 개발자가 될 수 있어요! 미국 개발자들을 열받게 하는 코드 패턴 공개.](https://youtu.be/ixOk13jC50w?si=fAOotcSJAd8jalAd) + diff --git a/_posts/2023-12-08-GithubActions.md b/_posts/2023-12-08-GithubActions.md new file mode 100644 index 0000000..bb305d4 --- /dev/null +++ b/_posts/2023-12-08-GithubActions.md @@ -0,0 +1,129 @@ +--- +layout: post +title: Github Actions +author: 이종찬 +categories: 기술세미나 +banner: + image: https://github.com/Kernel360/blog-image/blob/main/2023/1208/1.png?raw=true + background: "#000" + height: "100vh" + min_height: "38vh" + heading_style: "font-size: 4.25em; font-weight: bold; text-decoration: underline" + tags: [GithubActions] +--- + +목차 +- 목표 +- CI / CD란? +- GithubActions란? + - workflow? + - 사용해야하는 이유? + - 장점 + - 단점 +- 정리 + +## 목표 + +CI / CD, Github Actions이 무엇이고, 어떻게 사용할 수 있는지, 그리고 어떤 장단점이 있는지를 알고 사용하는 것을 목표로 글을 작성합니다. + +## 1. CI / CD란? + +CI/CD는 Continuous Integration과 Continuous Delivery 또는 Continuous Deployment의 약자로, 소프트웨어 개발에서 흔히 사용되는 방법론입니다. + +### 1-1. Continuous Integration + +![](https://github.com/Kernel360/blog-image/blob/main/2023/1208/2.png?raw=true) + +CI는 지속적인 통합이라는 뜻으로, 개발자들이 코드를 자주 병합하고, 빌드하고, 테스트하는 과정을 말합니다. CI를 통해 코드의 품질을 높이고, 버그를 줄이고, 협업을 쉽게 할 수 있습니다. + +### 1-2. Continuous Deployment + +CD는 지속적인 배포 또는 지속적인 전달이라는 뜻으로, CI의 결과물을 자동으로 배포하거나, 배포 준비 상태로 만드는 과정을 말합니다. CD를 통해 배포의 속도와 안정성을 높이고, 고객의 피드백을 빠르게 반영할 수 있습니다. + +![](https://github.com/Kernel360/blog-image/blob/main/2023/1208/3.png?raw=true) + +### 1-3. 장점 + +CI / CD 환경이 필요한 이유는 반복 작업의 자동화 및 피드백 루프 단축 등을 통해 소프트웨어 릴리스 프로세스의 속도를 개선하는 것 입니다. + +![](https://github.com/Kernel360/blog-image/blob/main/2023/1208/4.png?raw=true) + +짧은주기의 개발단위를 반복하며, 많은 협력과 피드백을 필요로 하는 애자일의 원칙을 실현하는 데 핵심적인 역할을 합니다. +![](https://github.com/Kernel360/blog-image/blob/main/2023/1208/5.png?raw=true) + +## 2. Github Action + +Github Actions은 Github에서 제공하는 CI/CD 도구입니다. + +Github 저장소에서 발생하는 다양한 이벤트에 따라 원하는 작업을 자동화할 수 있으며 workflow라는 단위로 구성되어있습니다. + +![](https://github.com/Kernel360/blog-image/blob/main/2023/1208/1.png?raw=true) + +### 2-1. Workflow + +Workflow는 Github Actions에서 자동화할 수 있는 작업의 흐름을 의미합니다. ./github/workflows 폴더에 저장되며 다음과 같은 요소로 구성됩니다. + +- name: Workflow의 이름을 지정합니다. (선택 사항) +- on: Workflow를 트리거할 이벤트를 지정합니다. (필수 사항) +- jobs: Workflow에서 실행할 Job들을 지정합니다. (필수 사항) +- env: Workflow 전체에서 사용할 환경 변수를 지정합니다. (선택 사항) + +```yml +# Workflow 이름 +name: Java CI with Gradle + +# workflow trigger +on: + push: + branches: [ "develop" ] + +# permission +permissions: + contents: read + +# Job +jobs: + build: + + # Job 실행 환경 + runs-on: ubuntu-latest + + # Job Step + steps: + # Step Action + - uses: actions/checkout@v3 + - name: Set up JDK 17 + uses: actions/setup-java@v3 + + # Step Input + with: + java-version: '17' + distribution: 'temurin' + - name: Run chmod to make gradlew executable + # Step 커맨드 + run: chmod +x ./gradlew + + - name: Build with Test + run: ./gradlew clean build test + +``` + +### 2-2. 장점 + +- Github Actions를 사용하면 다음과 같은 이유로 CI/CD를 쉽고 편리하게 구현할 수 있습니다. +- Workflow를 YAML 파일로 간단하게 정의하고, 코드와 함께 버전 관리할 수 있습니다. +- Github 마켓플레이스에서 수많은 Action을 찾아서 사용할 수 있으며, 자신이 만든 Action을 공유하거나 재사용할 수 있습니다. +- Workflow를 트리거할 수 있는 이벤트의 범위가 넓습니다. + +### 2-3. 단점 + +- Workflow의 실행 시간과 리소스에 제한이 있습니다. +- Workflow의 실행 환경에 따라서, 도커 컨테이너로 작성한 Action을 사용할 수 없는 경우가 있습니다. +- Workflow의 실행 결과와 로그를 Github 외부에서 확인하거나, 공유하기 어려울 수 있습니다. +- Workflow의 테스트와 디버깅을 위한 툴이 부족할 수 있습니다. + +## 정리 + +- CI / CD는 지속적인 통합과 지속적인 배포를 의미하며, 소프트웨어 출시 및 운영에 필요한 반복적인 작업을 빠르게 안정적으로 가져가기 위해 필요 +- Github Action은 CI / CD 환경 구축에 대한 리소스를 최소화 하며 사용할 수 있으며 트리거 할 수 있는 이벤트 범위가 넓기 때문에 사용 +- Github Actions의 단점은 workflow에 대한 테스트 및 디버깅을 할 수 있는 수단이 부족하며 외부에서 로그 확인이 힘듬 diff --git a/_posts/2023-12-13-Docker.md b/_posts/2023-12-13-Docker.md new file mode 100644 index 0000000..54949c0 --- /dev/null +++ b/_posts/2023-12-13-Docker.md @@ -0,0 +1,124 @@ +--- +layout: post +title: 도커 찍먹하기 +author: 김현지 +categories: 기술세미나 +banner: + image: https://raw.githubusercontent.com/Kernel360/blog-image/main/2023/1213/docker.png + background: "#000" + height: "100vh" + min_height: "38vh" + heading_style: "font-size: 4.25em; font-weight: bold; text-decoration: underline" + tags: [도커, docker, 쿠버네티스] +--- + +안녕하세요, 저는 김현지입니다. 이번에는 '도커 찍먹하기'라는 주제로 기술 세미나 발표를 하게 되었습니다. +도커를 선택한 이유는 저의 파이널 프로젝트를 배포할 때 도커를 사용할 것이기 때문입니다. +이에 따라 도커에 대한 이해와 익숙함을 기를 필요가 있다고 판단하였습니다. +이 포스팅은 '도커'에 대한 깊은 원리를 다루는 것이 아니라 소개 수준으로 이루어질 것이니, 편안하게 따라와 주시면 감사하겠습니다. + +처음에는 도커가 무엇인지부터 시작해서, 개발과 배포 과정에 어떻게 변화를 가져오는지 살펴보겠습니다. +## 도커란 무엇일까요? +도커는 컨테이너를 생성하고 실행할 수 있는 생태계입니다. **생태계**라는 표현을 강조한 이유는 도커와 컨테이너가 동치관계가 아니기 때문입니다. +도커는 컨테이너를 생성하고 관리하는 플랫폼으로, 이미지 생성, 컨테이너 오케스트레이션, 네트워킹 관리 등을 포함하는 포괄적인 개념입니다. +## 도커를 사용하는 이유는? + +소프트웨어 개발을 위해 도커를 사용하는 이유는 여러 가지가 있습니다. +일반적으로 소프트웨어 개발은 개발, 테스트, 배포, 운영 단계를 거칩니다. +때때로 애플리케이션은 로컬 환경에서는 잘 작동하지만 테스트 환경에서는 문제가 발생할 수 있습니다. +이는 환경 설정이나 의존성의 문제 때문일 수 있습니다. +따라서 도커를 사용하여 일관된 환경을 구성하고 애플리케이션을 격리하여 테스트하는 것이 중요합니다. + +![1](https://raw.githubusercontent.com/Kernel360/blog-image/main/2023/1213/virtualBox.jpg) + +일관된 환경을 구성하기 위해 도커는 가상 머신을 사용합니다. +가상 머신은 컴퓨터 하드웨어 리소스의 추상화된 개념으로, 다양한 운영 체제와 애플리케이션을 동시에 실행할 수 있도록 도와줍니다. +가상 머신을 사용하면 개발 환경과 테스트 환경을 동일하게 설정할 수 있습니다. + +![2](https://raw.githubusercontent.com/Kernel360/blog-image/main/2023/1213/hypervisor.png) + +가상 머신을 구성하는 과정에서 가상화 기술이 중요한데, 이는 물리적인 컴퓨터 자원을 가상적으로 분할하여 여러 개의 가상 컴퓨터 환경을 만들어내는 기술입니다. 가벼운 보따리인 컨테이너는 이러한 가상화 기술을 더욱 발전시킨 것입니다. +컨테이너는 전체 운영 체제를 포함하지 않고도 애플리케이션을 격리하여 실행할 수 있습니다. + +![3](https://raw.githubusercontent.com/Kernel360/blog-image/main/2023/1213/container.png) + +도커는 컨테이너 기반의 가상화 도구로, 애플리케이션을 컨테이너 단위로 격리하여 실행하고 배포할 수 있습니다. +이를 통해 애플리케이션을 손쉽게 빌드, 배포, 관리할 수 있습니다. +따라서 도커는 단순히 컨테이너를 실행하는 도구 이상의 플랫폼으로 발전하게 됩니다. + +## 도커의 아키텍처 +![4](https://raw.githubusercontent.com/Kernel360/blog-image/main/2023/1213/dockerArchitecture.png) + +도커의 아키텍처는 각각 특별한 역할을 하는 구성요소들로 이루어져 있습니다. +그 중 핵심 구성 요소는 **도커 엔진**입니다. +이번에는 도커 엔진에 대해 자세히 살펴보겠습니다. + +도커 엔진은 크게 세 가지 섹션으로 나뉩니다. + +첫 번째로, **도커 CLI**는 사용자가 웹에서 요청을 보낼 때와 마찬가지로 `run`, `pull`, `rm `등의 도커 명령이 도커 클라이언트로 전달됩니다. + +두 번째로, **도커 데몬**은 도커 프로세스가 실행되어 서버로서 입력을 받을 준비가 된 상태를 나타냅니다. +이는 도커 엔진의 핵심이며, 클라이언트의 요청을 받아들이고 처리하는 역할을 합니다. + +세 번째로, **도커**는 도커 CLI 요청이 도커 데몬과 통신하기 위해 자체적인 API를 제공합니다. + +![5](https://raw.githubusercontent.com/Kernel360/blog-image/main/2023/1213/clientServerArchitecture.png) + +이러한 클라이언트와 서버 아키텍처를 다시 한 번 살펴보면, 사용자가 `docker run`, `docker build`, `docker pull`과 같은 요청을 보내면 이것들은 도커 호스트의 데몬에서 처리됩니다. +이 과정에서 데이터 볼륨, 네트워크 관리, 도커 이미지 생성, 그리고 이미지를 통해 컨테이너를 생성하는 작업이 수행됩니다. +각 작업마다 엔드포인트가 있으며, 따라서 도커는 많은 엔드포인트를 가지고 있습니다. + +## 도커 이미지와 컨테이너 + +도커 이미지와 컨테이너에 대한 개념을 정리해보겠습니다. +**도커 이미지**는 소프트웨어를 실행하는 데 필요한 모든 것을 포함하는 패키지입니다. +이는 자바의 클래스 파일과 유사한 개념으로, 소프트웨어의 실행에 필요한 모든 설정, 종속성, 코드 등을 담고 있습니다. +반면에, **컨테이너**는 도커 이미지의 인스턴스로, 이미지를 실행한 상태입니다. +클래스 파일을 실행하여 여러 개의 객체(인스턴스)를 생성하는 것과 비슷하게, 도커 이미지로부터 여러 개의 컨테이너를 생성할 수 있습니다. + +![6](https://raw.githubusercontent.com/Kernel360/blog-image/main/2023/1213/dockerFile.png) + +이러한 개념을 이해하기 위해 **도커 파일**이 등장합니다. +도커 파일은 도커 이미지를 빌드하기 위한 지침서로, 이미지를 어떻게 구성할지를 정의합니다. +즉, 도커 파일은 이미지의 빌드 과정을 자동화하고 이미지가 어떻게 구성되는지를 명시합니다. + +이제 비즈니스 관점에서 살펴보겠습니다. +예전에 진행한 해외 모임지갑 서비스가 성공적으로 운영되었고, 이제 해외 숙박 예약 서비스를 추가하여 비즈니스를 확장하려고 합니다. +이로 인해 사용자 트래픽이 증가할 것으로 예상되는데, 이에 대비하여 서비스의 안정성과 응답성을 유지하기 위해 도커 컨테이너를 스케일 아웃하여 서비스를 확장해보고자 합니다. + +즉, 도커를 사용하여 현재 운영 중인 서비스를 여러 개의 컨테이너로 병렬로 실행하고, 트래픽이 증가할 경우 추가적인 컨테이너를 동적으로 생성하여 부하를 분산하고 응답성을 유지하려는 것입니다. +도커를 사용하면 이러한 확장이 비교적 쉽게 이루어질 수 있으며, 서비스의 안정성을 보장할 수 있습니다. + +## 도커 컴포즈 +**도커 컴포즈**는 여러 개의 컨테이너로 구성된 도커 애플리케이션을 쉽게 정의하고 실행하기 위한 도구입니다. +이를 통해 yaml 파일을 사용하여 서비스, 네트워크, 볼륨 등을 구성할 수 있습니다. +주요 장점은 단일 명령어로 모든 구성 요소를 시작하거나 중단할 수 있다는 것입니다. +`docket compose up`은 실행시키는 명령어이며, `docker compose down`은 중단하는 명령어입니다. + +그러나 서비스가 대박나면서 트래픽이 급증하여 컨테이너 증설만으로는 해결할 수 없는 상황이 발생했습니다. +이에 대응하기 위해 서버를 확장해야 할 필요가 생겼습니다. 이 때 선택할 수 있는 방법은 Scale Up과 Scale Out입니다. +**Scale Up**은 기존 서버의 성능을 높이는 것이며, **Scale Out**은 여러 대의 서버를 병렬적으로 활용하는 것입니다. + +그러나 현재 상황에서는 스타트업이므로 재빠르게 대응할 필요가 있으며, 비용을 고려하여 Scale Out 방식이 적합해 보입니다. +이를 통해 서버를 병렬적으로 확장하여 트래픽을 효과적으로 처리할 수 있을 것으로 기대됩니다. + +## 컨테이너 오케스트레이션 + +**컨테이너 오케스트레이션**은 여러 대의 서버를 클러스터로 만들어 자원을 병렬로 확장하는 것을 의미합니다. +이를 위해 도커에 내장된 도커 스웜을 소개하겠습니다. + +![7](https://raw.githubusercontent.com/Kernel360/blog-image/main/2023/1213/dockerSwarm.png) + +**도커 스웜**은 컨테이너 수를 동적으로 조절하여 로드 밸런싱 기능을 수행합니다. +그러나 사용자가 인스턴스의 개수를 수동으로 조절하는 스케일 아웃 방식을 사용하기 때문에 수작업이 필요합니다. +도커 스웜은 매니저 노드와 워커 노드로 구성되어 있으며, 매니저 노드는 워커 노드의 기능을 수행하면서 관리도 함께 합니다. + +![8](https://raw.githubusercontent.com/Kernel360/blog-image/main/2023/1213/kubernetes.png) + +또 다른 컨테이너 오케스트레이션 방법으로 **쿠버네티스**를 소개할 수 있습니다. +쿠버네티스는 컨테이너화된 애플리케이션의 배포, 확장 및 운영을 자동화합니다. +도커 스웜보다 더 정교한 자동화를 가능하게 해주며, 서비스의 복제 수를 자동으로 조절할 수 있습니다. + +컨테이너 오케스트레이션 도구를 선택할 때는 프로젝트의 규모와 요구 사항을 고려해야 합니다. +도커 스웜은 작고 가벼운 프로젝트에서 손쉽게 사용할 수 있으며, 쿠버네티스는 복잡한 환경에서 세밀한 관리가 필요할 때 사용하는 것이 좋습니다. + diff --git a/_posts/2024-01-10-better-test-framework.md b/_posts/2024-01-10-better-test-framework.md new file mode 100644 index 0000000..2892451 --- /dev/null +++ b/_posts/2024-01-10-better-test-framework.md @@ -0,0 +1,93 @@ +--- +layout: post +title: 더 나은 테스트로 인도해줄 친구들 +author: 조형준 +categories: 기술세미나 +banner: + image: https://github.com/Kernel360/blog-image/blob/main/2024/0110/1.png?raw=true + background: "#000" + height: "100vh" + min_height: "38vh" + heading_style: "font-size: 4.25em; font-weight: bold; text-decoration: underline" + tags: [Mockito, Jacoco, 테스트코드, 기술세미나] +--- +## 개요 +안녕하세요, 커널 360 2차 기술세미나의 포문(for문아님)을 열게된 조형준입니다. +저번 포스팅에선 클린코드에 관한 내용을 다뤘는데요, 이번 포스트는 더 좋은 테스트코드를 도와주는 프레임워크를 소개해드리겠습니다! + +## 테스트코드, 이제 좀 짰어요! +![2.png](https://github.com/Kernel360/blog-image/blob/main/2024/0110/2.png?raw=true) +ControllerTest.. +ServiceTest.. +RepositoryTest.. 등등.. +여러 테스트코드를 작성했겠다. 이제 좀 테스트코드를 알 것 같아요! + +과연 그럴까? + +![3.png](https://github.com/Kernel360/blog-image/blob/main/2024/0110/3.png?raw=true) +![4.png](https://github.com/Kernel360/blog-image/blob/main/2024/0110/4.png?raw=true) +![5.png](https://github.com/Kernel360/blog-image/blob/main/2024/0110/5.png?raw=true) + +네.. 테스트 커버리지는 박살이 났습니다. 이대로 출시하게 된다면 뭐.. 결과는 뻔하겠죠 + +![6.png](https://github.com/Kernel360/blog-image/blob/main/2024/0110/6.png?raw=true) + +## 테스트도 일이다. 효율적으로 하자. +이러나저러나, 테스트코드도 어쨌든 공수입니다. 따라서 우리는 빠르게 테스트코드를 작성하고, 테스트 커버리지도 효율적으로 올릴 방법을 알아보도록 합시다. + +### Mockito +우린 지금까지 실제 객체를 매핑하고, DB 커넥션까지 실제 DB에 연결해서 사용했었습니다. +하지만, 이러한 테스트 방식을 모든 테스트코드에 적용한다면 다음과 같은 이슈가 있습니다. + +- 실제 객체를 테스트에 사용하면 너무 복잡해지고 의존성이 높아져.. +- 테스트를 할 때마다 Spring application을 올리기엔 너무 오래걸림.. +- 기존 가짜(Mock)객체 프레임워크는 어렵고.. 문법도 복잡해! + +![7.png](https://github.com/Kernel360/blog-image/blob/main/2024/0110/7.png?raw=true) +이를 해결하기 위해 현재 자바진영에선 가장 대표적인 Mockito 프레임워크를 사용하고 있습니다. +실제 객체 대신 Mock객체를 설정해, 테스트를 빠르고 간결하게 도와줍니다! +Mock이 무엇인가? 에 대해선 제 [개인 블로그](https://kkkapuq.tistory.com/146)에 작성해두었으니 참고하시면 좋을듯합니다. + +한마디로 요약하자면, Mock은 프록시, 즉 가짜 객체를 만들기 때문에 리소스 효율이 좋고, 성능이 빠르기에 행위 검증의 단위테스트에 매우 적합합니다. +간단하게 사용 방법을 알아보시죠. 간단한 PostDto 객체를 생성해줍니다. + +![8.png](https://github.com/Kernel360/blog-image/blob/main/2024/0110/8.png?raw=true) + +이후 Mockito의 when 문법으로, `findById()`를 호출하면 `existingPostDto.entity()`가 와야해! 라고 선언해줍니다. + +![9.png](https://github.com/Kernel360/blog-image/blob/main/2024/0110/9.png?raw=true) + +이처럼 어떤 행위의 인과관계에 대해 명시적으로 선언해주고, 실제로 실행시켰을 때 프로그램이 해당 로직을 정상적으로 수행하면, 테스트에 성공하게 됩니다. +Mockito의 활용법에 대해선 많은 자료가 있으니, 본 포스팅에선 따로 다루지 않도록 하겠습니다. + +## 테스트 커버리지는 어캄? +![10.png](https://github.com/Kernel360/blog-image/blob/main/2024/0110/10.png?raw=true) +이 IntelliJ는 무료로 해줍니다. +IntelliJ 한정으로 무료로 제공해줍니다만, 매우 부실합니다. 간편하지만, 그만큼 커버리지 검증이 약합니다. + +> 테스트할 패키지 우클릭 +> More Run/Debug +> Run ‘~~’ with Coverage + +### Jacoco +![11.png](https://github.com/Kernel360/blog-image/blob/main/2024/0110/11.png?raw=true) +`Java Code Coverage`의 약자로, 자바의 대표적인 커버리지 체크 라이브러리입니다. +커버리지 기준을 설정할 수 있고, (테스트 커버리지 80% 미만이면 빌드가 안되게 한다던가) +html, xml, csv 등 파일로 출력이 가능해서, 현업에서도 많이 쓰고 있는 오픈소스입니다! +세팅 방법은 [여기](https://techblog.woowahan.com/2661/) 에서 확인 후 적용해보시면 좋을 듯 합니다. + +테스트코드를 실행하고, build/jacoco/index.html의 파일을 실행시켜보면, 이렇게 퍼센티지로 확인이 가능합니다. +![12.png](https://github.com/Kernel360/blog-image/blob/main/2024/0110/12.png?raw=true) + +이런식으로 코드 내 메서드 단위로도 확인이 가능하기 때문에, 좀 더 디테일한 테스트코드 작성이 가능해집니다. + +![13.png](https://github.com/Kernel360/blog-image/blob/main/2024/0110/13.png?raw=true) +![14.png](https://github.com/Kernel360/blog-image/blob/main/2024/0110/14.png?raw=true) + +- 초록색 : 테스트가 진행 된 부분 +- 노란색 : 조건, 결정 커버리지가 모두 충족되지 않은 부분 +- 빨간색 : 커버리지가 진행되지 않은 부분 + +## 마치며 +이번 포스팅에선 테스트를 조금 더 윤택하고, 퀄리티있게 작성해주는 Mockito / Jacoco에 대해 알아봤습니다. +Jacoco의 유일한 단점... 그것은 커버리지 100%를 채우지 못하면 불-편해짐... diff --git a/_posts/2024-01-17-docker-replication.md b/_posts/2024-01-17-docker-replication.md new file mode 100644 index 0000000..5034096 --- /dev/null +++ b/_posts/2024-01-17-docker-replication.md @@ -0,0 +1,169 @@ +--- +layout: post +title: 배포있게 배포하기 -DB편- +author: 손민우 +categories: 기술세미나 +banner: + image: https://github.com/Kernel360/blog-image/blob/main/2024/0117/1.png?raw=true + background: "#000" + height: "100vh" + min_height: "38vh" + heading_style: "font-size: 4.25em; font-weight: bold; text-decoration: underline" + tags: [Docker, AWS, DB] +--- + +다시 돌아온 Docker 세미나 시간입니다. 어쩌다보니 오픈세미나에 이어서 이번 주제도 Docker와 관련한 주제로 선택을 하게 되었는데요. (정말 파도 파도 공부해야 하는 부분이 계속 나오더라구요.) + +이전 오픈 세미나에서는 Docker의 기본적인 내용을 설명을 했다면 오늘 설명드릴 내용은 DB replication을 Docker를 통해서 적용해 보고 이를 쉽게 적용할 수 있도록 나온 서비스인 AWS RDS를 + +통해서 이해하는 시간을 가져보려고 합니다. 그럼 시작해보겠습니다. + +## 1. Docker-Compose는 무엇인가? + +
+image +
+ +아마 Docker를 공부하다 보면 Docker-Compose에 대해서 이름은 들어보셨을 거라 생각됩니다. 제가 처음 Docker-Compose를 접하게 되었을 때 느꼈던 점은 사용만 할 줄 안다면 좋은 툴이 될 것 +같은데 그 과정이 쉽지 않을 것 같다는 생각이 정말 많이 들었습니다. + +그래서 이러한 생각을 좀 줄여 보고자 MySQL을 예로 들어 Docker-Compose를 작성해 보겠습니다. + +## 2. Docker-Compose로 MySQL 실행하기 + +
+image +
+ +다음 사진은 MySQL을 실행시키기 위한 docker-compose.yml 파일의 내용입니다. 다음은 설명입니다. +|옵션|설명| +|------|---| +|image|docker를 실행할 image:tag를 설정합니다. ex) ubuntu, linux, mysql, postgresql ...| +|ports|port를 설정하는 옵션으로 [host port]:[container 내부 port]를 의미합니다.| +|environment|container를 실행할 때 환경 변수를 추가한 상태로 실행할 수 있습니다. 각 이미지마다 환경 변수가 다를 수 있어 확인한 후 사용해야 합니다. ex) MYSQL_ROOT_PASSWORD: 1234 | +|command|컨테이너를 실행 한 후 내부에서 명령어를 실행해 적용할 수 있습니다.| +|volumes|마운트를 지정하게 되면 이후 컨테이너가 종료되거나 삭제되더라도 이후 연결되는 컨테이너가 같은 경로에 마운트 된다면 이전 데이터를 받을 수 있습니다.| + +어떤가요? 이렇게 보면 docker-compose라는 것은 단순하게 script의 형태를 띄기 때문에 각각의 의미만 안다면 어렵지 않습니다. 이제 간단하게 해봤으니 DB replication도 진행해보도록 하겠습니다. + +## 3. MySQL DB replication 구성해보기 + +DB replication을 진행하기 전에 간단하게 replication에 대해 알아보겠습니다. + +DB replication은 데이터 저장과 백업하는 방법에 관련이 있는 데이터를 호스트 컴퓨터에서 다른 컴퓨터(컨테이너)로 복사하는 것을 의미합니다. 그렇다면 왜 필요할까요? + +서비스를 운영함에 있어서 읽기에 대한 요청은 읽기 이외의 요청 (생성, 수정, 삭제)에 비해 많은 비중을 가지게 됩니다. 그렇기 때문에 읽기에 대한 요청을 전담하여 맡아줄 DB를 생성해 DB로 하여금 부하를 +감소시킬 수 있도록 역할을 할 수 있습니다. + +
+image +
+ +다음 사진은 이제 진행할 db replication의 파일 구조입니다. +위 파일 구조는 master-slave가 1 대 1로 되어 있지만 설정에 따라 N 대 N이 될 수도 있습니다. + +![image.jpg1](https://raw.githubusercontent.com/Kernel360/blog-image/main/2024/0117/5.png) ![image.jpg2](https://raw.githubusercontent.com/Kernel360/blog-image/main/2024/0117/6.png) + +위의 스크립트는 순서대로 master-db와 slave-db의 Docker-Compose의 내용이다. 위에서 설명하지 않는 옵션은 다음과 같습니다. + +|옵션|설명| +|------|---| +|build| context는 build를 실행한 경로를 dockerfile은 dockerfile이 위치한 경로를 입력해주면 됩니다.| +|restart|재시작과 관련한 옵션으로 현재 항상 재시작한다는 옵션으로 지정되어 있습니다.| +|networks|docker에는 network를 통해 컨테이너 간의 연결을 진행하고 데이터를 주고받을 수 있으며 기본값은 bridge network입니다.| + +docker-compose.yml파일 외에도 my.cnf와 Dockerfile이 있는데 이때 Master와 Slave의 차이는 파일 경로와 읽기 전용 옵션 외에는 동일하여 Master를 기준으로 보겠습니다. + +
+image +
+ +Dockerfile에서 내용은 다음과 같습니다. + +|옵션|설명| +|------|---| +|FROM|컨테이너의 환경을 지정하는 옵션으로 mysql이미지를 실행한다는 의미입니다.| +|ADD|host에 위치한 파일을 컨테이너 내부 위치에 추가한다는 의미입니다. mysql의 configure 파일을 교체하여 내가 정한 규칙대로 실행되도록 합니다.| + +my.cnf 파일의 내용입니다. + +
+image +
+ +현재 있는 파일에서 초록색 표시가 되어있는 read_only 옵션은 Slave-db가 가지고 있는 옵션으로 읽기 전용으로 사용하기 위한 옵션입니다. + +이렇게 준비가 되었다면 docker-compose up -d 명령어를 통해 실행을 시키면 다음과 같은 log와 설정을 볼 수 있습니다. + +
+image +
+ +성공적으로 build가 되었다면 이제 db로 접속하여 설정을 진행해주어야 합니다. + +
+image +
+ +replication에 대한 유저를 생성하고 권한을 부여하는 과정입니다. + +
+image +
+ +권한 부여 이후에는 Slave DB에서 Master DB로 바라볼 수 있도록 설정을 수정합니다. + +
+image +
+ +설정을 진행한 이후 아래 명령어를 통해 동일한 내용이 나온다면 성공적으로 설정된 것을 볼 수 있습니다. + +
+image +
+ +Docker를 사용하여 replication을 진행하는 과정은 이렇게 마무리할 수 있습니다. 긴 과정일 수 있지만 실제로는 dockerfile과 docker-compose.yml, my.cnf만 여러 개 둔다면 +규모가 큰 DB replication도 가능하기 때문에 오히려 구성하기 어렵지 않았다고 생각됩니다. + +하지만 개발자 입장에서 개발에 더 몰입할 수 있도록 간단하게 구성할 수 있는 서비스가 AWS RDS입니다. + +## 4. AWS RDS란? + +AWS RDS(Amazon Relational Database Service)는 aws에서 제공되는 서비스로 MySQL 뿐만 아니라, PostgreSQL, Aurora, Oracle 등 여러 DB를 간단하게 구현할 수 있습니다. +단일 구성 뿐 아니라 위에서 진행했던 DB replication도 보다 쉽게 구성할 수 있습니다. 그럼 AWS RDS로도 DB replication을 구성해 보도록 하겠습니다. + +## 5. AWS RDS replication 구성해보기 + +처음 AWS에 접속하여 RDS로 이동하면 다음과 같은 화면을 볼 수 있고 데이터베이스 생성 버튼을 누릅니다. + +
+image +
+ +그 다음 여러 엔진 유형 중 제가 생성하고자 하는 MySQL을 선택하고 Database명, 비밀번호 등을 설정합니다. + +
+image +
+ +이때 외부에서 접속하고자 한다면 퍼블릭 엑세스를 예로 채크해야 합니다. 이렇게 설정을 마치고 생성을 진행하면 5 ~ 10분 정도를 기다리면 생성이 완료 됩니다. + +
+image +
+ +생성이 완료되면 생성된 DB를 선택하고 작업에서 읽기 전용 복제본 생성을 진행하고 위의 생성과정을 반복합니다. + +
+image +
+ +이렇게 하면 자동으로 RDS 내에서 master와 slave가 적용되며 db를 동일하게 백업하고 저장되는 것을 확인할 수 있습니다. +Docker를 사용하는 것도 간단해 보였지만 AWS RDS를 사용하니 더욱 간단하게 구현을 마무리할 수 있었습니다. + +## 6. 마무리 + +결론은 AWS RDS로 구현하는 것이 간단하다가 아닌 Docker를 통해서 구성하는 것을 토대로 AWS RDS를 이해하는 시간이 되었으면 좋겠습니다. (물론 개념적인 부분에 대한 설명이 부족하지만 ...) +이러한 과정을 반복하는 것 만으로도 전체적인 흐름을 이해하는데 도움이 될 것이라고 생각합니다. +끝까지 읽어주셔서 감사합니다. diff --git a/_posts/2024-01-18-plugins-to-improve-productivity.md b/_posts/2024-01-18-plugins-to-improve-productivity.md new file mode 100644 index 0000000..cd3654f --- /dev/null +++ b/_posts/2024-01-18-plugins-to-improve-productivity.md @@ -0,0 +1,108 @@ +--- +layout: post +title: 생산성을 도와줄 프로그램들 +author: 이종찬 +categories: 기술세미나 +banner: + image: https://github.com/Kernel360/blog-image/blob/main/2024/0118/1.png?raw=true + background: "#000" + height: "100vh" + min_height: "38vh" + heading_style: "font-size: 4.25em; font-weight: bold; text-decoration: underline" + tags: [기술세미나] +--- + +# 생산성 향상을 위한 도구들 + +## 생산성이란? + +- 좋은 개발 환경 구축 : 효율적인 작업 환경 ex)IDE, 버전관리 도구, 디버깅 도구... +- 자동화 : 시간을 소모하는(많이)작업을 자동화 하여 시간 단축 +- 코드 품질 관리 : 코드 리뷰, 정적 분석 도구 활용 +- 학습과 성장 : 최신 개발 트렌드 지식 유지 및 기술 향상 +- 작업 관리 : 우선 순위 설정과 같은 체계적으로 단계를 형성하여 관리 + +이중에서 시간을 아낄 수 있는 효율에 초점을 맞추어 글을 작성하도록 하겠습니다. + +## 목표 + +![](https://github.com/Kernel360/blog-image/blob/main/2024/0118/1.png?raw=true) + +## 목차 +- JPA Buddy +- IntelliJ Live Template +- Github Copilot +- Octotree +- 마치며 + +## 1. JPA Buddy + +![](https://github.com/Kernel360/blog-image/blob/main/2024/0118/2.png?raw=true) + +JPA Buddy는 자바 개발자를 위한 플러그인으로, IntelliJ IDEA에서 엔티티 클래스와 매핑 파일을 자동으로 생성하고 관리할 수 있습니다. +JPA Buddy는 데이터베이스 스키마와 동기화하고, 쿼리를 테스트하고, JPA 관련 코드를 쉽게 작성할 수 있도록 도와줍니다. + +### Table -> Entity +![](https://github.com/Kernel360/blog-image/blob/main/2024/0118/4.png?raw=true) +![](https://github.com/Kernel360/blog-image/blob/main/2024/0118/5.png?raw=true) + +### Entity -> DTO +![](https://github.com/Kernel360/blog-image/blob/main/2024/0118/6.png?raw=true) +![](https://github.com/Kernel360/blog-image/blob/main/2024/0118/7.png?raw=true) + +JPA Buddy를 사용하여 DB에 있는 Table을 Entity로, Entity를 DTO로 변환할 수 있는 것을 확인할 수 있습니다. +약간 아쉽지만 column이 많을 수록 불필요한 작업을 안해도 된다는 점은 좋아보입니다. 아쉬운 부분은 뒷부분에서 채워보겠습니다. + +--- +## 2. IntelliJ Live Template + +IntelliJ Live Template은 IntelliJ IDEA에서 반복적인 코드를 작성할 때 시간을 절약할 수 있는 기능입니다. +Live Template은 미리 정의된 코드 조각을 키워드와 함께 저장하고, 필요할 때 키워드를 입력하면 코드 조각이 자동으로 삽입되는 방식으로 작동합니다. +개발을 하다보면 반복하는 작업이 많습니다. 아래와 같은 작업을 템플릿화 해놓으면 실수도 줄이고 시간 절약도 할 수 있습니다. + +### LiveTemplate을 이용한 Entity Template +![](https://github.com/Kernel360/blog-image/blob/main/2024/0118/9.png?raw=true) +![](https://github.com/Kernel360/blog-image/blob/main/2024/0118/10.png?raw=true) + +### JpaBuddy X LiveTemplate + +그렇다면 두개의 플러그인을 잘 활용하면 어떻게 생산성을 높일 수 있을까요? +![](https://github.com/Kernel360/blog-image/blob/main/2024/0118/110.png?raw=true) + +### Notice + +실질적으로 사용할 수 있는 코드를 쉽고 빠르게 작성할 수 있습니다. +![](https://github.com/Kernel360/blog-image/blob/main/2024/0118/12.png?raw=true) + +이러한 프로그램들의 도움이 없었다면 +같은 결과를 도출해도 소모되는 시간이 크게 차이가 나게 됩니다. + +--- +## 3. Github Copilot + +GitHub에서 2021년 출시한 서비스로 개발자가 직접 개발한 코드를 AI가 이해하여 다음 작성 코드를 자동 완성해서 먼저 제시해주는 기능입니다. +코드 에디터에 통합되어 개발자가 작성하는 코드를 분석하고, 적절한 코드 조각을 제안하며,다양한 언어와 프레임워크를 지원합니다. + +### Github Copilot 적용 모습 +![](https://github.com/Kernel360/blog-image/blob/main/2024/0118/11.png?raw=true) + +앞서 배운 JPA Buddy 플러그인으로 DTO를 만들고 Copilot이 제안한 코드가 나오는 모습입니다. +첫 사용에서는 코드AI가 좋지 못하지만 비슷한 유형의 코드를 반복하다보면 원하는 코드를 쉽게 작성할 수 있습니다. + +## 4. Octotree +![](https://github.com/Kernel360/blog-image/blob/main/2024/0118/15.png?raw=true) + +Octotree는 GitHub, GitLab, Bitbucket 등의 코드 호스팅 플랫폼에서 프로젝트를 탐색할 때 유용한 플러그인입니다. +웹 브라우저에서 코드 저장소의 파일 구조를 트리 형태로 보여주고, 원하는 파일을 빠르게 열 수 있으며 +다크 모드, 코드 검색, 풀 리퀘스트 리뷰 등의 기능도 제공합니다. + +기존의 Github에서 코드를 열람하려면 다른 코드로 이동할 때마다 새로고침하며 불편하게 열람했지만 +Octotree를 사용하면 새로고침 없이 레포지토리의 코드를 쉽게 파악할 수 있는 것을 확인할 수 있습니다. +![](https://github.com/Kernel360/blog-image/blob/main/2024/0118/16.png?raw=true) + +## 마치며 + +![](https://github.com/Kernel360/blog-image/blob/main/2024/0118/220.png?raw=true) + +이 밖에도 많은 생산성에 도움을 주는 도구들이 있습니다. +어떻게 하면 생산성을 높일 수 있을까? 라는 생각이 개발을 하는데 많은 도움이 된다고 생각하여 글을 작성하였고 많은 도움이 되었으면 좋겠습니다. diff --git a/_posts/2024-01-24-spring-rest-docs.md b/_posts/2024-01-24-spring-rest-docs.md new file mode 100644 index 0000000..4dc3c08 --- /dev/null +++ b/_posts/2024-01-24-spring-rest-docs.md @@ -0,0 +1,272 @@ +--- +layout: post +title: 이번엔 Spring REST Docs를 써볼까? +author: 김영롱 +categories: 기술세미나 +banner: + image: https://github.com/Kernel360/blog-image/blob/main/2024/0124/spring-rest-docs.png?raw=true + background: "#000" + height: "100vh" + min_height: "38vh" + heading_style: "font-size: 4.25em; font-weight: bold; text-decoration: underline" +tags: [API 문서, Spring REST Docs, 기술세미나] +--- +안녕하세요, *Lune* 입니다. + +이번엔 **Spring REST Docs**를 기술 세미나 주제로 가져와봤는데요. + +Spring REST Docs를 프로젝트에서 사용하게 된 이유부터 적용기까지 공유해보려고 합니다. + +## 1. API 문서의 필요성 +조금 뻔한 얘기로 시작해볼까요?
+개발을 하면서 API 문서들이 필요한 이유는 무엇일까요? 제가 생각해봤을 때, 그리고 검색을 해봤을 때 아래와 같은 이유들이 있었습니다. + +1. API 사용법 이해 +2. 개발자 간의 효율적인 협업 +3. 빠른 문제 해결 및 버그 대응 +4. 개발 생산성 향상 + +개인적으로는 일단 2번이 제일 와닿습니다. 협업할 때 같은 API 문서를 보며 얘기하면 소통이 더 잘된다고 느낀 적이 많았거든요. + +## 2. API 문서를 만드는 여러 방법들 +자, 그럼 이번에는 API 문서를 만들 수 있는 방법들에 대해 이야기해볼게요. + +1. 구글 공유 문서 (Docs / Sheets) +2. Notion +3. GitBook +4. Swagger +5. Spring REST Docs + +위에 리스트 말고도 더 많은 방법들이 있겠지만, 제가 사용해봤거나 사용사례를 본 적 있는 것 위주로 작성해봤습니다. + +### 구글 공유 문서 / Notion / GitBook +코드베이스가 아니라 수동으로 직접 문서를 작성하는 방식입니다. 장단점을 알아보자면 다음과 같습니다. + + 장점 | 단점 +-----------------|------------------ + 쉬운 협업 및 공유 | API 문서 작성 기능 제한적 + 커스텀 스타일 적용 쉬운 편 | 비용 문제 발생 가능 + 사용자 친화적인 UI/UX | API 문서 자동화 어려움 + +위 내용이 3가지 모두에 동일하게 적용된다고 할 수는 없지만, 보편적인 장단점 위주로 설명해보려고 했으니 조금 안 맞다고 생각이 들어도 이해해주세요 👐 + +### Swagger / Spring REST Docs +코드 내에 API 문서화를 위한 작업을 진행하는 방식입니다. 장단점을 알아보자면 다음과 같습니다. + + 장점 | 단점 +----------------|----------------- + 자동 문서 생성 | 러닝커브 존재 및 설정 복잡 + 일관된 형식과 스타일 유지 | 커스터마이제이션 한계 + 실시간 업데이트 | 의존성 및 업그레이드 어려움 + +여기서 *커스터마이제이션 한계* 란 문서의 외관이나 기능을 개발자가 원하는 대로 조정하는 데에 한계가 있다는 것을 의미합니다. + +## 3. Spring REST Docs를 선택한 이유 +현재 진행 중인 프로젝트에서는 왜 Spring REST Docs를 선택하게 되었을까요? + +우선 첫 시작은 GitBook 이었답니다. 기능 개발이 들어가지 않은 상태에서 프론트엔드 개발자와의 빠른 협업을 위해 GitBook으로 API 문서를 만들기 시작했었죠. 그런데 초반에 잘 알아보지 않은 탓에 얼마 지나지 않아 무료 버전의 한계를 맞닥뜨리게 되었습니다. 무료 버전에서는 여러 사람이 문서 편집을 할 수 없어 공동 작업이 더 이상 불가능하게 되어버렸거든요 🥲 + +그 다음으로 생각한 건 API 문서 자동화가 가능한 Swagger와 Spring REST Docs였습니다. 그래서 그 둘을 비교해봤습니다. + +### Swagger + + 장점 | 단점 +----------------|--------------------------- + 어노테이션 기반 문서 생성 | 프로덕션 코드에 작업 필요 + 화면에서 API 테스트 | 프로덕션 코드와 동기화 안 되어 있을 수 있음 + 비교적 쉬운 적용 | + +### Spring REST Docs + + 장점 | 단점 +-----------------|--------------- + 프로덕션 코드에 영향 없음 | 적용이 어려운 편 + 테스트 성공 시 문서 생성 | 테스트 코드 양이 늘어남 + API 문서 최신 상태 유지 | + +(제목에 드러나있듯이) 결과적으로 Spring REST Docs를 사용하기로 결정을 했습니다. 이번 프로젝트에서는 테스트 코드를 작성하기로 했었고, 프로덕션 코드에 영향이 없고 늘 현행화가 가능하다는 점에 이끌렸거든요. + +## 4. Spring REST Docs 적용기 +이제부터는 Spring REST Docs를 적용했던 과정을 하나씩 설명해보겠습니다.
+참고로 저는 Asciidoctor & MockMvc 방식을 사용했는데요. 공식 문서에 따르면 문서 작성을 위해 Asciidoctor와 Markdown을 지원하고 있고 MockMvc, WebTestClient, REST Assured 방식의 테스트 예시를 보여주고 있습니다. + +### 1) build.gradle 설정 +build.gradle에 추가되어야 하는 내용 및 설명은 아래 코드로 대체합니다. +```groovy +plugins { + // Asciidoctor 플러그인 적용 + id "org.asciidoctor.jvm.convert" version "3.3.2" +} + +ext { + // 생성된 snippets 출력 위치를 정의하는 snippetsDir 속성을 구성 + snippetsDir = file('build/generated-snippets') +} + +configurations { + // asciidoctorExt 구성을 선언 + asciidoctorExt +} + +dependencies { + // asciidoctorExt 구성에서 spring-restdocs-asciidoctor에 대한 종속성을 추가 + asciidoctorExt 'org.springframework.restdocs:spring-restdocs-asciidoctor' + // MockMvc 테스트 방식을 사용 + testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc' +} + +tasks.named('test') { + // 테스트 작업을 실행하면 출력이 snippetsDir에 기록된다는 것을 Gradle이 인식하도록 함 + outputs.dir snippetsDir +} + +asciidoctor { + // asciidoctor 작업을 구성 + dependsOn test + configurations 'asciidoctorExt' + baseDirFollowsSourceFile() + inputs.dir snippetsDir +} + +asciidoctor.doFirst { + // asciidoctor 작업이 수행될 때 가장 먼저 수행 + delete file('src/main/resources/static/docs') +} + +task copyDocument(type: Copy) { + // html 파일 복사 + dependsOn asciidoctor + from file("build/docs/asciidoc") + into file("src/main/resources/static/docs") +} + +bootJar { + dependsOn copyDocument +} +``` + +### 2) 테스트 코드 작성 +첫 번째 *테스트 코드* 외에는 구현 방식에 따라 다를 수 있으므로 가볍게 봐주시면 됩니다. +```java +/** 테스트 코드 **/ +result.andExpect(status().isOk()) + .andDo(document( + "commoncode/get-common-codes", + getDocumentRequest(), + getDocumentResponse(), + // pathParameters, queryParameters, requestFields, responseFields는 필요 시 각각 작성 + pathParameters( + parameterWithName("codeName").description("코드명") + ), + responseFields(beneathPath("value").withSubsectionId("value"), + fieldWithPath("codeNo").type(JsonFieldType.NUMBER).description("코드번호"), + fieldWithPath("codeName").type(JsonFieldType.STRING).description("코드명"), + fieldWithPath("description").type(JsonFieldType.STRING).description("설명").optional() + ) +)); +``` +```java +/** Utils 만들어 사용 **/ +public interface RestDocumentUtils { + + static OperationRequestPreprocessor getDocumentRequest() { + return preprocessRequest(modifyUris().scheme("http") + .host("spring.restdocs.test") // 실제 host 아님 + .removePort(), prettyPrint()); + } + + static OperationResponsePreprocessor getDocumentResponse() { + return preprocessResponse(prettyPrint()); + } +} +``` +```java +/** 추상 클래스 작성 **/ +@WebMvcTest({ + CommonCodeController.class, +}) +@AutoConfigureRestDocs // 통합 테스트에서 Spring REST Docs를 활성화하고 구성하는 데 사용 +public abstract class ControllerTest { + + @Autowired + protected MockMvc mockMvc; + + @Autowired + protected ObjectMapper objectMapper; + + @MockBean + protected CommonCodeService commonCodeService; +} + +// 아래와 같이 상속받아 사용 +// class CommonCodeControllerRestDocsTest extends ControllerTest { +``` + +### 3) 테스트 성공 +위와 같이 작성한 테스트 코드가 통과하게 된다면 build/generated-snippets 경로 하위에 adoc 확장자 파일들이 여러 개 생성된 것을 확인할 수 있습니다. 파일을 하나씩 선택해서 보면 Asciidoc 문법에 맞게 작성된 내용과 미리보기를 확인할 수 있답니다. + +![gradle-build](https://github.com/Kernel360/blog-image/blob/main/2024/0124/gradle-build.png?raw=true) + +### 4) API 문서 틀 작성 +위에서 본 문서 조각들(snippets)을 그대로 활용할 수 있다면 좋겠지만, HTML 형태의 API 문서로 만들어 주기 위해서는 아직 추가적인 작업이 남아있습니다. 한 데 모아주는 작업을 해줘야 하는데요. 우선 저는 *index.adoc* 이라는 파일을 만들어 API별 adoc 파일을 include 하는 구조를 사용했습니다. 참고로 index.adoc 파일은 *src/docs/asciidoc 하위* 에 만들어주었는데 이 경로가 gradle을 사용할 경우의 기본 경로랍니다. + +```asciidoc +// index.adoc += API Document +// Asciidoc 문서의 구조와 스타일 정의 +:doctype: book +:icons: font +:source-highlighter: highlightjs +:toc: left +:toclevels: 2 +:sectlinks: + +// adoc 파일 include +include::overview.adoc[] +include::sample-api.adoc[] +``` +```asciidoc +// sample-api.adoc +== 샘플 API + +[[공통코드-조회]] +=== 공통코드 조회 + +==== Request +include::{snippets}/commoncode/get-common-codes/path-parameters.adoc[] + +===== HTTP Request 예시 +include::{snippets}/commoncode/get-common-codes/http-request.adoc[] + +==== Response +include::{snippets}/commoncode/get-common-codes/response-fields-value.adoc[] + +===== HTTP Response 예시 +include::{snippets}/commoncode/get-common-codes/http-response.adoc[] +``` + +### 5) API 문서 생성 +이제 거의 마무리 단계인데요. gradle의 bootJar 작업을 실행시키면 build.gradle에 작성한 copyDocument 작업을 거쳐서 미리 지정한 resources/static/docs 경로 하위에 build 내에 생성되어 있던 html 파일이 복사되어 들어오고, 서버를 띄웠을 때 도메인 하위 /docs/index.html 경로로 접속해 API 문서를 확인할 수 있게 된답니다. + +![api-doc](https://github.com/Kernel360/blog-image/blob/main/2024/0124/api-doc.png?raw=true) + +### 6) API 문서 살펴보기 +최종적으로 만들어진 문서의 형태는 다음과 같습니다. 좌측에 ToC가 있어 원하는 API로의 이동이 간편하고 화면도 깔끔하지 않나요? + +![index-html](https://github.com/Kernel360/blog-image/blob/main/2024/0124/index-html.png?raw=true) + +## 5. 정리 +지금까지 저와 함께 API 문서부터 Spring REST Docs 적용기까지 살펴봤는데요. 읽기에 어떠셨을지 궁금합니다 😄 + +Spring REST Docs를 적용하면서 여러 차례 시행착오를 거치며 보낸 시간이 생각나네요. 처음 접하는 부분이 많아 공식 문서, 영상, 블로그 등을 참고했는데 그 과정에서 조금씩 이해하게 되고 원하는 결과를 만들어낼 수 있어서 개인적으로 뿌듯하고 좋았던 경험이었습니다. + +아 그리고 이번 세미나를 준비하면서 추가적인 정보를 찾다가 알게 된 건데 Swagger와 Spring REST Docs의 장점만 취해서 API 문서화를 할 수 있는 방법도 있다고 해요. 가능하다면 다음에는 이 방식을 사용하거나 시간이 될 때 변경해볼 수 있으면 좋을 것 같다는 생각이 드네요. + +## 6. 참고자료 +- [Spring REST Docs](https://docs.spring.io/spring-restdocs/docs/current/reference/htmlsingle/) +- [Spring REST Docs에 날개를… (feat: Popup)](https://techblog.woowahan.com/2678) +- [Spring REST Docs를 사용한 API 문서 자동화](https://hudi.blog/spring-rest-docs) +- [RestDocs로 API 문서화하기](https://velog.io/@junho5336/RestDocs%EB%A1%9C-API-%EB%AC%B8%EC%84%9C%ED%99%94%ED%95%98%EA%B8%B0) +- [[ 스프링 기반 REST API ] 스프링 REST Docs 소개](https://youtu.be/BFme2t9KSwA?si=ziyQ3jC1l-tQ57Md) +- [[10분 테코톡] 승팡, 케이의 REST Docs](https://youtu.be/BoVpTSsTuVQ?si=VG3mhS5b32l1EY_s) +- [[Spring] restdocs + swagger 같이 사용하기](https://velog.io/@hwsa1004/Spring-restdocs-swagger-%EA%B0%99%EC%9D%B4-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0) diff --git a/_posts/2024-01-25-coding-test.md b/_posts/2024-01-25-coding-test.md new file mode 100644 index 0000000..41c7c80 --- /dev/null +++ b/_posts/2024-01-25-coding-test.md @@ -0,0 +1,147 @@ +--- +layout: post +title: 주저하는 개발자들을 위해 - 코딩 테스트 +author: 고병룡 +categories: 기술세미나 +banner: + image: https://www.hanbit.co.kr/data/editor/20200918163925_xyypndmo.png + background: "#000" + height: "100vh" + min_height: "38vh" + heading_style: "font-size: 4.25em; font-weight: bold; text-decoration: underline" + tags: [코딩테스트, 기술세미나] +--- + +## 코딩 테스트? +코딩 테스트란 개발자의 기술적인 역량을 시험하기 위해 실시하는 알고리즘이나 구현을 요구하는 테스트다. + +회사 입장에서는 지원자를 평가하는 최소한의 기준이고 지원자에게는 ~~통곡의 벽~~ 처럼 느껴지는 코딩 테스트 대체 왜 해야할까? + +그리고 또 어떻게 준비해야만 할까..? + +## 코딩 테스트 그래서 왜 해야하나? +무엇보다도, 많은 회사들이 코딩테스트를 1차적인 관문으로 두고 지원자를 거르고 있기 때문에 그런 회사들에 지원할 생각이 있다면 꾸준히 준비하는 게 좋다. + +> 아래 4가지 이유를 통해서 조금 더 자세히 알아보자면.. + +* 여러분들이 가고 싶어하는 많은 회사에는 정말 많은 지원자가 몰린다. 이들 개개인의 모든 역량을 테스트하기에는 현실적으로 무리가 있고, 이 과정에서 코딩 테스트는 효율적으로 이들을 가려내는데 도움이 되기 때문에 많은 기업들이 도입한다. +* 여러분들이 어떤 식으로 코드를 작성하는지 미리 확인하고 어떤 사고 방식과 결론, 평소 개발을 어떻게 하는지 엿볼 수 있는 창구와 비슷하다. 자신이 어떤 개발자인지 비교적 손 쉽게 다른 개발자들에게 보여줄 수 있다. +* 개발에 필요한 지식들을 보여주고 나의 컴퓨터 과학 지식을 기반으로 한 나의 문제 해결력을 보여주기 쉽다. 결국 알고리즘 문제를 구현한다는 건 그 문제를 이해해야만 할 수 있는 거고, 그에 맞는 알고리즘을 찾고 시간 내에 테스트 케이스들을 통과시켜야 하기 때문에, 비교적 쉽게 내 문제 해결 능력을 보여줄 수 있다. +* 개발자는 자주,, 생각보다 자주 이직이라는 카드를 꺼내든다.. 이직할 때 코테 준비를 하지말고,, 항상 이직이라는 카드를 꺼낼 수 있도록 언제든 코테를 준비를 할 필요가 있다. + +## [당신이 코딩테스트를 포기하는 7가지 이유](https://www.inflearn.com/course/%EA%B0%9C%EB%B0%9C%EC%9E%90-%EC%B7%A8%EC%97%85-%ED%86%B5%ED%95%A9%ED%8E%B8) +아래의 7가지 이유는 [한정수님](https://github.com/Integerous)의 [비전공자를 위한 개발자 취업 가이드](https://www.inflearn.com/course/%EA%B0%9C%EB%B0%9C%EC%9E%90-%EC%B7%A8%EC%97%85-%ED%86%B5%ED%95%A9%ED%8E%B8)의 내용에서 가져온 개발자들이 코테를 포기하는 7가지 이유이다. + +가져온 이유들에는 각각 논리적인 허점이 존재하고 우리는 같이 그것을 깨부수고 코딩테스트를 준비하기 위한 마음가짐을 잡아 보도록 하자. + +>1. 코딩 테스트를 보는 회사에는 어차피 서류 합격도 못할 것이다. + +아니다. 서류만 내도 코테를 보게 해주는 회사가 많고 “서류”도 영향이 있지만 “테스트 결과” 도 영향이 있다. +> 2. 코딩 테스트를 준비할 시간이 없다. + +분명히 당신의 하루에는 낭비되는 시간이 있다. 그 시간을 찾아서 하자. 코딩 테스트 준비 뿐만아니라 다른 공부도 마찬가지다.. +> 3. 지금부터 준비하더라도, 더 오래 준비한 전공자들에게 밀릴 것이다. + +체육 전공한 사람이 마라톤을 더 잘 뛸까? 아니면 일반인이 더 잘 뛸까? 확률은 전자가 높겠지만 거의 비슷하게 힘들다. 전공자들이라고 해서 코테가 쉽고 뚝딱 풀리는 일은 아니다. +> 4. Java(언어), Spring(프레임워크)도 제대로 못하는데 무슨 코딩 테스트? + +코딩 테스트라도 제대로 해야지 면접은 볼 수 있지 않을까? 물론 둘 다 잘하면 정말 좋고 그것도 아니라면 면접에 들어갈 수 있는 실력이 된다는 것 자체로도 의미가 있다고 생각한다. 코테를 볼 기회는 항상 찾아오는게 아니니... +> 5. 코딩 테스트 안봐도 개발로 취업은 하더라. + +당연히 할 수 있다. 하지 않는 기업을 찾아서 지원하면 된다. 하지만 차라리 코테를 준비하고 더 많은 기회를 얻는게 맞지 않나 라는 생각이 든다. +>6. 조금 해봤는데, 너무 어렵다. + +착각하는 분들이 있는데 우리는 대회를 나가자고 하는 게 아니다. 코테를 쳐보는 것만으로도 소중한 자산이다. 이걸로 상을 타는 게 아니라 우리는 취업이 목적이기에.... +>7. 어떻게 준비해야 될지 모르겠다. + +그러면 이제부터 알아보자! 만약 필자가 뭐하는 사람인지 의심이 간다면.. 그래도 나름 [열심히(?)](https://solved.ac/profile/hodako97) 준비해오고 [알고리즘을 좋아하고](https://github.com/fingersdanny/PS_note), [알고리즘 스터디](https://github.com/Kernel360-cell1/algorithm-study)를 운영 중인 사람 중 하나로서 여러분들이 코테에서 더 행복해지길 바라는 마음에서 준비했다. + +## 코딩테스트 어떻게 준비해야 할까? + +### 1. 알고리즘 / 자료구조를 배우자 + +코딩 테스트에 나오는 문제는 다양한 배경 지식을 요구하고 그 중 만나게 되는 대부분은 자료 구조와 다양한 알고리즘이다. + +따라서, 무턱대고 문제를 풀기위해 접근 하는 것보다는 미리 아래의 자료구조와 알고리즘과 친숙해진 이후로 풀어보길 시작하는 것을 권장한다.. + +그 중에 보편적으로 활용되는 자료 구조와 알고리즘은 다음과 같다. + +#### 자료구조 +* 배열 (Array) +* 리스트 (List) +* 연결 리스트 (LinkedList) +* 스택 (Stack) +* 큐 (Queue) +* 덱 (Deque) +* 우선순위 큐 (Priority Queue), 힙 (Heap) +* 트리 (Tree), 이진 트리(Binary Tree) +* 그래프 (Graph) +* 세트 (Set) +* 맵 (Map) +* 서로소 집합 (Disjoint Set / Union Find) + +> 위에 있는 자료 구조들을 모두 아는 편이 좋은가요? + +위 자료 구조들은 기술 면접 때도 자주 만날 수 있으니 말로 설명할 수 있을 만큼 알아 두는 편이 좋다. 당연히 문제를 만났을 때 위의 자료구조를 각 프로그래밍 언어별로 어떤 식으로 사용할 수 있는지도 알아두어야 문제에 적용할 수 있다. + +#### 알고리즘 +* 그리디 (Greedy) +* 동적 계획법 (Dynamic Programming) : 메모이제이션... +* 다양한 정렬 알고리즘 : 버블, 선택, 삽입, 병합, 힙, 퀵 +* 탐색 알고리즘 + * 깊이 우선 탐색 (DFS) + * 너비 우선 탐색 (BFS) + * 이진 탐색 + * 매개 변수 탐색 + * 트리에서의 탐색 : 힙 트리, 트라이(Trie) +* 크루스칼 알고리즘 (최소 신장 트리) +* 백트래킹 +* 최단 경로 : 다익스트라, 벨만-포드, 플로이드-워셜 +* 완전 탐색 (Bruteforcing) + +> 알고리즘과 마찬가지로 위에 있는 내용들도 전부는 아니지만 말로 설명할 수 있어야 한다. + +> 알고리즘 공부를 시작하고 싶은데 어디서부터 보는게 좋을까요? + +개인적으로 방법은 중요하다고 생각하지 않고 꾸준히 보는게 더 낫다고 생각하지만 그래도 몇 가지 강의 및 책을 추천해보자면, + +[이것이 코딩 테스트다](https://product.kyobobook.co.kr/detail/S000001810273?utm_source=google&utm_medium=cpc&utm_campaign=googleSearch&gad_source=1&gclid=EAIaIQobChMIutbEu_OphAMVE1cPAh1V2A1hEAAYASAAEgLDevD_BwE) + +[BaaaaaaaarkingDog님의 실전 알고리즘 강좌](https://blog.encrypted.gg/919) + + +### 2. 꾸준히 풀자 + +너무나도 당연한 말이지만 생각보다 코딩 테스트는 언제든 볼 수 있게끔 준비를 해놓는 상태가 훨씬 좋다고 생각한다. 꾸준히 준비하지 않으면 실력이 떨어지는 속도는 가파르지만 실력을 올리는 속도는 생각보다 더 걸릴 수 있다. + +따라서 사람들이 자주 사용하고, 실제 코딩 테스트 환경에 가까운 곳에서 자주 문제를 풀어보기를 권장한다. + +* [프로그래머스](https://school.programmers.co.kr/learn/challenges?order=recent) : 많은 기업들의 코딩 테스트를 진행해주고 있고 문제도 꾸준히 올라온다. 카카오 등의 기업 공채에서의 기출 문제 또한 제공하고 있다. +* [SW Expert Academy](https://swexpertacademy.com/main/main.do) : 삼성에서 운영하는 코딩테스트 및 알고리즘 학습 사이트이다. 삼성 관련 (반기 마다 열리는 알고리즘 특강, 입사 시 코딩 테스트)가 모두 여기에서 요구하는 문제 입출력 방식이나 라이브러리를 어떤 것을 사용할 수 있는지 까지 미리 알고 준비할 수 있다. +* [Baekjoon Online Judge](https://www.acmicpc.net/) : 다양한 문제를 제공하고 삼성 SW 역랑 기출 문제를 제공하고 있다. 압도적인 문제 수와 유저 수로 인해 문제를 풀지 못했을 때 물어볼 곳이 더 많다는 이점이 있다. + +개인적으로는 백준을 제일 좋아하고 애용한다. 꾸준히 풀기를 지키기 위해서는 나름의 보상이 필요하다고 생각하는데.. 필자는 꾸준히 풀기 위해 코딩 테스트를 나름의 게임이라고 생각하여 위 백준 사이트와 연동되는 [solved.ac](https://solved.ac/)를 활용하여 꾸준히 잔디 심기(?) 및 랭크를 통해서 코딩 테스트를 준비중이다. + +### 3. 실제로 구현하기 전에 어떻게 구현할 지 손으로 적어보자 + +아는 문제가 나와서 구현부터 할 생각할지 말고 무엇부터 어떻게 구현할지 생각해보고 문제 풀이를 작성해보자. + + 1. 요즘 코딩 테스트에 나오는 문제들은 설명이 길다. 미리 순서를 정해두지 않고 구현만 하다가 보면 중간에 갈 길을 잃기 쉽고 문제 설명 중 어디까지 구현했는지 추적할 수가 없다. 결국 다시 되돌아가서 문제를 읽어야 할 수 도 있다. 처음부터 모든 상황을 본인의 언어로 정리해둔다면, 헷갈릴 이유도 없고 본인이 어디까지 구현했는 지 추적이 가능하다. + 2. 대부분의 코딩 테스트는 펜과 종이를 허용한다. 이걸 준비하라고 하는 이유는 써서 고민해봐도 좋다는 얘기고 이것을 활용하지 않을 이유가 없다! + 3. 많은 수의 기업이 코딩 테스트에 대한 기출문제를 제공하지 않는다. 따라서 직접 적어 놓은 문제 구현 계획을 보고 나서 나중에 복기할 수 있도록 한다. + +### 4. 시간 분배를 잘하자. + +1. 문제를 풀기 전에 시간 복잡도 계산을 해보고 올바른 알고리즘을 골랐는지 생각해보자. 보수적으로 생각했을 때 연산이 1억번을 1초라 가정하면 대부분의 문제에서 시간 초과를 받지 않을 수 있다. 어떤 알고리즘을 사용할지 입력의 최대 범위는 어디까지인지 시간은 얼마나 주어졌는지를 복합적으로 고려해보고 문제를 풀어야 한다. +2. 실제 시험도 마찬가지지만 문제 당 시간 분배가 중요하다. 최악의 경우 2시간 내에 4문제를 풀어야할 수도 있고 그거 보다 길거나 짧을 수 있다. 평균적으로 실전에서는 40분 ~ 1시간이 주어진다고 생각하고 문제를 접근해야 테스트 중 시간이 모잘라서 문제를 포기하는 불상사를 겪지 않을 수 있다. +3. 여느 시험을 볼 때와 마찬가지로 너무 오래 걸리는 문제만 붙잡고 있지 말아야 한다. 모든 답안을 다 들고 시험장에 갈 수 없듯이 코딩테스트도 못 푸는 문제가 있을 수 있다. 그럴 땐 넘기고 시간이 남을 때 돌아보는게 정신건강에 훨씬 더 좋다. +4. 앞의 내용의 연장선에서 대부분의 코딩테스트는 순서대로 풀지 않아도 된다. 처음에 문제를 받는다면 쭉 훑어보고 해당 문제의 알고리즘이 바로 보이거나 구현이 오래 걸리지 않을 것 같은 문제들을 빨리 풀고 시간을 아끼는 게 좋다! + + +## 참고 자료 +이것이 코딩 테스트다, 나동빈 저, pg 46 - 50 + +[개발자 취업을 위한 코딩 테스트 준비 방법](https://katfun.tistory.com/192) + +[알고리즘 분류 참고 - 백준 온라인 저지 알고리즘 별 문제 분류](https://www.acmicpc.net/problem/tags) + diff --git a/_posts/2024-01-26-stomp-websocket-programing.md b/_posts/2024-01-26-stomp-websocket-programing.md new file mode 100644 index 0000000..177798b --- /dev/null +++ b/_posts/2024-01-26-stomp-websocket-programing.md @@ -0,0 +1,147 @@ +--- +layout: post +title: STOMP 웹소켓 프로그래밍 +author: 문찬욱 +categories: 기술세미나 +banner: + image: https://raw.githubusercontent.com/Kernel360/blog-image/main/2024/0126/thumb.png + background: "#000" + height: "100vh" + min_height: "38vh" + heading_style: "font-size: 4.25em; font-weight: bold; text-decoration: underline" + tags: [웹소켓, STOMP] +--- + +안녕하세요. 저는 STOMP 웹소켓 프로그래밍이라는 주제로 발표한 문찬욱입니다. + +저는 이번 프로젝트에서 채팅 기능을 구현하고 있는데요, 그 과정에서 사용한 기술에 대해 공유드리고자 이 주제를 선정했습니다. + +이 글에서 웹소켓이 무엇인지, http와 웹소켓의 차이점이 무엇인지, 웹소켓 동작 방식은 어떤지, STOMP이 무엇인지 알려드리고자 합니다. + +
+ +## 1. 웹소켓 +웹소켓은 http 환경에서 클라이언트와 서버 사이에 하나의 TCP 커넥션을 통해 실시간으로 전이중 통신을 가능하게 하는 프로토콜입니다. + +여기서 전이중 통신은 전화기처럼 양방향으로 송신과 수신이 가능한 통신을 뜻합니다. + +
+ +## 2. HTTP vs 웹소켓 +그럼 이러한 웹소켓과 HTTP와의 차이점은 무엇일까요? + +Http는 클라이언트가 요청을 보낼 때마다 연결을 맺고 응답을 받은 후 연결을 끊어버리기 때문에 비 연결성 프로토콜입니다. + +그리고 클라이언트가 요청하고 서버가 응답하는 단방향 통신입니다. + +반면에 웹소켓은 한번 연결을 맺으면 어느 한쪽에서 연결을 끊으라는 요청을 보내기 전까진 연결을 유지하기 때문에 연결 지향 프로토콜입니다. + +그리고 양 쪽에서 데이터를 주고 받을 수 있는 양방향 통신입니다. + +![1](https://github.com/Kernel360/blog/assets/97713997/2d25b3e0-d31f-426b-b69d-001cbcf00d3a) + +### 실시간 측면에서는...? +http는 비 연결성이기 때문에 데이터를 주기 위해선 매번 연결해야 하고 그만큼 연결 비용이 발생하게 됩니다. + +즉, 오버헤드가 커집니다. + +![2](https://github.com/Kernel360/blog/assets/97713997/580a3e5c-29bc-429e-85c5-8e248e4db002) + +위 이미지에서 왼쪽은 일반적인 http로 주고받는 데이터양이고, 오른쪽 위는 웹소켓 연결요청할 때의 데이터양이고, 그 아래는 연결 이후의 데이터양입니다. + +웹소켓은 처음 연결할 때 http로 하기 때문에 처음 데이터 양은 유사하지만, 이후 데이터 양이 매우 줄어든 것을 확인할 수 있습니다. + +이러한 점들에서 웹소켓이 http 보다 실시간성에 더 유리합니다. + +## 3. 웹소켓 동작 방식 +![3](https://github.com/Kernel360/blog/assets/97713997/344e5c48-0ead-40f7-bc5c-bc3a9b3f4c4b) + +웹소켓의 동작 방식은 크게 3가지로 나눌 수 있는데요, + +빨간 박스는 Opening Handshake, + +노란 박스는 Data transfer, + +보라 박스는 Closing Handshake에 해당됩니다. + +### Opening Handshake +![4](https://github.com/Kernel360/blog/assets/97713997/5995bac3-ca2c-4ad7-83d0-ad1d5cce8ad8) + +빨간 박스 부분인 Opening Handshake는 먼저 웹소켓 클라이언트에서 http 프로토콜을 통해 웹소켓으로 Upgrade 해달라는 요청을 보냅니다. + +이 요청의 의미는 웹소켓 프로토콜로 전환해달라는 것입니다. + +그 다음 웹소켓 서버는 101 응답과 함께 웹소켓 프로토콜로의 전환을 승인했음을 알립니다. + +### Data Transfer +![5](https://github.com/Kernel360/blog/assets/97713997/61f2f6b2-c9e7-4436-ba0c-34a75d78e5bb) + +노란 박스 부분인 Data Transfer는 Opening Handshake가 끝나서 전환된 웹소켓 프로토콜을 통해 클라이언트와 서버 간 실시간 전이중 통신을 하는 부분입니다. + +### Closing Handshake +![6](https://github.com/Kernel360/blog/assets/97713997/50c700a8-ff7f-4b4a-a5a3-635d0d016a11) + +보라 박스 부분인 Closing Handshake는 서로 간의 커넥션을 종료하는 부분입니다. + +## 4. STOMP +STOMP는 streaming text oriented messaging protocol의 줄임말이며, + +메세지 브로커를 활용하여 쉽게 메시지를 주고 받을 수 있는 프로토콜입니다. + +이 프로토콜은 웹소켓 위에 얹어 하위 프로토콜로 함께 사용할 수 있습니다. + +### 웹소켓만 사용하는 것 비해 어떤 이점이...? +웹소켓 프로토콜은 메시지 데이터 타입이 Text인지 Binary인지는 정의하지만, 메시지 내용에 대해서는 정의하지 않습니다. + +이는 복잡한 메시지를 주고 받으려면 어떤 메시지인지 알기 위한 로직이 필요하다는 것입니다. + +STOMP 프로토콜은 메시지 내용을 구조화 할 수 있어서 효과적인 메시징을 도와줍니다. + +예를 들어, A라는 사람이 B라는 사람에게 “안녕”이라는 내용으로 보내는 메시지가 있다고 해보겠습니다. + +웹소켓로 보낼 때는 “안녕”이라는 메시지만 보내면 됩니다. + +클라이언트가 1명이라면 문제가 없지만, 클라이언트가 여러명이라면, 이 메시지를 누가 보냈는지, 누구한테 가야하는지, 어떤 용도로 보내는 것인지 서버가 알 수가 없습니다. + +물론, 단순히 안녕이 아닌 모든 정보가 담긴 한줄의 긴 텍스트 문자열로 보낼 수 있습니다. + +하지만 이러면, 서버가 이 메시지를 이해하고 처리하기 쉽지 않을 수 있습니다. + +또한 연결된 클라이언트 주소마다 메시지 핸들러를 구현해야 합니다. + +![7](https://github.com/Kernel360/blog/assets/97713997/527a4717-9c58-4402-8441-9c4ae7455555) + +STOMP는 이미지처럼 + +destination /app/chat/message로 보내는, +sender A가, + +content “안녕”의 내용의 메시지를, + +send 전송했음을 + +구조화 된 형태로 보낼 수 있기 때문에 서버는 이 메시지가 어떤 메시지인지 이해하고 처리하기 쉽습니다. + +또 다른 이점은 STOMP는 pub/sub 구조로 되어있다는 것입니다. + +![8](https://github.com/Kernel360/blog/assets/97713997/23a3e3f0-b8f9-4156-a15a-76bf8cef85bf) + +pub/sub 구조란 이 이미지처럼 publisher에서 메시지 브로커의 Topic1이라는 특정 토픽에 메시지를 보내면, Topic1을 구독하고 있는 subscribe들에게 메세지를 전달하는 구조를 말합니다. + +이 구조로 인해 최대 토픽별로만 메시지 핸들러를 구현해주면 되기 때문에 웹소켓로만 구성된 것에 비해 메시지 핸들러 구현 양이 줄어든다는 이점이 있습니다. + +### pub/sub 구조의 특징 +저는 pub/sub 구조의 특징을 크게 3가지로 나눠볼 수 있었습니다. + +첫째, pub과 sub을 쉽게 추가할 수 있어서 확장성이 좋습니다. + +둘째, pub과 sub 간에 강한 의존성이 없어서 비동기적으로 처리할 수 있습니다. + +셋째, sub은 관심 토픽에 대한 메시지만 받을 수 있어서 선택적 수신이 가능합니다. + +
+ +## 5. 마무리 +채팅, 실시간 업데이트, 알림 같은 실시간성 기능을 구현하고자 하면, + +STOMP도 선택지 중 하나라고 생각합니다. diff --git a/_posts/2024-01-31-generics.md b/_posts/2024-01-31-generics.md new file mode 100644 index 0000000..7f41257 --- /dev/null +++ b/_posts/2024-01-31-generics.md @@ -0,0 +1,286 @@ +--- +layout: post +title: 자바 제네릭(Generic) - 제네릭 메서드와 타입 범위 한정 +author: 우무룡 +categories: 기술세미나 +banner: + image: https://github.com/Kernel360/blog-image/blob/main/2024/0131/GENERICS_THUMBNAIL.png?raw=true + background: "#000" + height: "100vh" + min_height: "38vh" + heading_style: "font-size: 4.25em; font-weight: bold; text-decoration: underline" + tags: [제네릭, 기술세미나] +--- + +안녕하세요 커널360 1기 크루 우무룡입니다. + +기술세미나 발표주제로 익숙하면서도 헷갈릴 수 있는 제네릭을 선정했습니다. +기본적인 개념에 대해 간단히 알아보고 개인적으로 헷갈렸던 제네릭 메서드와 타입 범위 한정에 대해 주로 알아보겠습니다! + + +## 제네릭이란 +자바에서 제네릭은 타입 안정성을 위해 jdk 1.5부터 도입된 문법이다. +클래스 내부에서 사용할 데이터 타입을 클래스 내부에서 선언하는 것이 아닌 외부에서 사용자에 의해 지정하는 기법이다. + +```java +ArrayList list = new ArrayList<>(); +``` +저 꺾쇠 괄호가 바로 제네릭이다. 괄호 안에는 타입명을 기재한다. 그러면 저 리스트 클래스 자료형의 타입은 String 타입으로 지정되어 문자열 데이터만 리스트에 적재할 수 있게 된다. + +이처럼 제네릭은 클래스나 메소드에서 사용할 내부 데이터 타입(type)을 파라미터(parameter) 주듯이 외부에서 지정하는 이른바 타입을 변수화 한 기능이라고 이해하면 된다. + +## 제네릭 클래스 +클래스 선언문 옆에 제네릭 타입 파라미터가 쓰이면, 이를 제네릭 클래스라고 한다. + +```java +class Sample { + private T value; // 멤버 변수 val의 타입은 T 이다. + + // T 타입의 값 val을 반환한다. + public T getValue() { + return value; + } + + // T 타입의 값을 멤버 변수 val에 대입한다. + public void setValue(T value) { + this.value = value; + } +} + +public class Main { + public static void main(String[] args) { + Sample sample = new Sample(); + //Sample sample = new Sample<>(); 생성자 부분 타입 생략가능 +} + +``` +제네릭 클래스의 타입 파라미터는 해당 클래스를 **인스턴스화 하는 시점**에 입력한 값을 받아와서 해당 파라미터가 지정된 곳으로 전파된다. jdk 1.7 이후부터는 생성자 부분의 제네릭 타입은 생략 가능하다. + +## 제네릭의 장점 +### 재사용성 +```java +ArrayList list1 = new ArrayList(); +ArrayList list2 = new ArrayList(); + +LinkedList list3 = new LinkedList(): +LinkedList list4 = new LinkedList(); +``` +흔히쓰는 ArrayList와 LinkedList를 예로 들었다. 위와 같이 ArrayList와 LinkedList에 다양한 타입을 사용할 때에도 IntegerArrayList나 StringArrayList 같은 클래스를 사용할 필요없이 ArrayList에 제네릭에 <원하는 타입>을 지정해 줌으로써 같은 클래스를 재사용할 수 있었다. + +제네릭을 사용하면 우리가 나중에 어떤 클래스를 작성할 때에도 지원하고자 하는 타입에 따라 모든 별도의 클래스를 만들필요 없이 사용할때 지정해주도록 해서 재사용성을 높일 수 있다. + +### 컴파일 타임 타입검사 + +제네릭이 없던 jdk 1.5 이전에는 하나의 클래스가 여러 타입을 다루기 위해서 모든 클래스의 조상인 Object 클래스를 인자와 반환값으로 사용했다. 반환된 Object를 원하는 타입으로 사용하기 위해서는 일일이 타입 변환을 해야했고, 그로 인해 런타임 에러가 발생할 가능성이 존재했다. +```java +List list = new ArrayList(); +list.add(new Apple()); +list.add(new Banana()); + +Apple apple = (Apple)list.get(0); +Apple apple1 = (Apple)list.get(1); // 런타임 에러! (ClassCastException) +``` +위 예시에는 ArrayList를 제네릭 없이 선언했고 그러면 인자와 반환값을 모두 Object로 사용한다. list에는 Apple만 들어있어야 했으나 개발자가 착각해서 Banana를 추가했다. 다시 객체를 가져올 때 Apple로 형변환 해 가져오려고 했기 때문에 런타임 에러인 ClassCastException이 발생한다. + + +```java +List list = new ArrayList(); +list.add(new Apple()); +list.add(new Banana()); // 컴파일 에러 + +Apple apple = list.get(0); // 불필요한 캐스팅 제거 +``` +제네릭을 사용하면 이런 실수를 사전에 방지할 수 있다. 코드를 실행하기 전 컴파일 타임에 에러를 찾아 알려주기 때문이다. 게다가 타입이 이미 정해져 있기 때문에 불필요하게 형변환을 해줄 필요가 없다. + + +## 제네릭 메소드 +제네릭 메서드란 클래스에 선언한 제네릭과 관계없이(혹은 클래스에서 선언하지 않더라도) 독립적인 제네릭을 메서드의 타입파라미터로 지정하는 메서드를 의미한다. +보통 아래 예시의 get, set 메서드처럼 클래스의 제네릭을 매개변수로 받거나 반환값으로 사용하는 메서드를 제네릭 메서드로 오해하기 쉽다. + +genericMethod처럼 메서드의 선언부에 별도의 타입파라미터를 선언한 메서드가 제네릭 메서드임을 주의하자. +```java +class ClassName { + + private E element; + + void set(E element) { + this.element = element; + } + + E get() { + return element; + } + + // 제네릭 메소드 + T genericMethod(T o) { + return o; + } + +} + +public class Main { + public static void main(String[] args) { + + ClassName a = new ClassName<>(); + a.set("10"); + + System.out.println("a data : " + a.get()); + System.out.println("a E Type : " + a.get().getClass().getName()); + + // 제네릭 메소드 Integer + System.out.println(" returnType : " + a.genericMethod(3).getClass().getName()); + // 제네릭 메소드 String + System.out.println(" returnType : " + a.genericMethod("ABCD").getClass().getName()); + } +} +``` +위 예시의 메인메서드와 아래의 실행결과를 보면 클래스의 제네릭과 독립적이라는 것이 어떤 의미인지 알 수 있다. +위 코드를 실행시키면 아래와 같이 출력된다. + +![](https://github.com/Kernel360/blog-image/blob/main/2024/0131/GENERICS_CLASSTYPE_CONSOLE.png?raw=true) + +'a' 인스턴스를 생성하면서 String으로 타입파라미터를 전달했다. E로 선언된 제네릭은 **인스턴스 생성시점**에 String으로 결정되어 E타입을 String으로 반환한다. + +그러나 genericMethod에 인자를 3이라는 정수로 전달하였더니 'a' 클래스를 생성할때 전달했던 타입과 관계없이 Integer를 반환한다. 제네릭 메서드의 타입은 제네릭 클래스와 달리 **메서드를 호출하는 시점에 결정**된다. +따라서, 클래스에 선언된 제네릭과 독립적으로 타입을 지정할 수 있다. + +그럼 제네릭 클래스의 타입을 가져다 사용하면 되는데 제네릭 클래스와 독립적인 타입을 선언해야 하는 이유는 뭘까? 바로 정적 메서드를 선언할 때는 꼭 필요하기 때문이다. + + +이전에 제네릭 클래스의 타입은 인스턴스화 할때 결정된다고 했다. 하지만 static 메서드는 인스턴스를 생성하기도 전에 프로그램 실행시 모두 스태틱 메모리 영역에 올라가게 된다. 그래서 인스턴스를 생성하지 않고 바로 사용할 수 있는데, 인스턴스가 생성되기 이전 시점에 제네릭 클래스로 부터 타입을 받아올 수 없다. 따라서, 제네릭이 사용되는 메소드를 정적메소드로 두고 싶은 경우 제네릭 클래스와 별도로 독립적인 제네릭이 사용되어야 한다. + +> 하지만 제네릭 메서드가 꼭 정적 메서드로만 선언되어야 하는 건 아니다. 인스턴스 메서드로 선언한다면 메서드 호출을 위해 클래스의 인스턴스를 생성해서 접근해야겠지만, +> 여전히 클래스에 선언된 제네릭(또는 선언되지 않았더라도)과 관계없이 독립적인 타입파라미터를 선언해 활용할 수 있다. + +## 제네릭 타입 범위 한정 + +제네릭을 사용하면 타입을 원하는 대로 자유롭게 지정할 수 있다는 것이 장점이지만 또, 너무 자유롭다는 것이 단점이 될 수도 있다. + +예를 들어, 개발자가 아래와 같은 계산기 클래스를 만들었다고 하자. 다양한 숫자타입의 계산을 지원하기 위해 제네릭 클래스로 선언했다. 하지만, 단순히  로 지정하게 되면 숫자에 관련된 래퍼 클래스 뿐만 아니라 String이나 다른 클래스들도 대입이 가능하다는 점이 문제다. + +```java +// 숫자만 받아 계산하는 계산기 클래스 모듈 +class Calculator { + void add(T a, T b) {} + void min(T a, T b) {} + void mul(T a, T b) {} + void div(T a, T b) {} +} + +public class Main { + public static void main(String[] args) { + // 제네릭에 아무 타입이나 모두 할당이 가능 + Calculator cal1 = new Calculator<>(); + Calculator cal2 = new Calculator<>(); + Calculator cal3 = new Calculator<>(); + Calculator
cal4 = new Calculator<>(); + } +} +``` + +개발자는 숫자타입만 지원하는 것이 의도였으므로 원하지 않는 다른 자료형은 할당하지 못하도록 제한하는 기능이 필요하다. 그래서 필요한 것이 제한된 타입 매개변수 (Bounded Type Parameter) 이다. +![](https://github.com/Kernel360/blog-image/blob/main/2024/0131/GENERICS_EXAMPLE.png?raw=true) +위와 같이 서로 다른 클래스들이 상속관계를 갖고 있다고 가정해보자. + +타입을 한정하는데 사용하는 키워드들은 다음과 같다 `extends`, `super`, `?` + +보통 이해하기 쉽게 다음과 같이 부른다. +extends : 상한 경계 +super : 하한 경계 +? : 와일드 카드(Wild card) + +```java + // B와 C타입만 올 수 있음 + // E타입만 올 수 있음 + // A, B, C, D, E 타입이 올 수 있음 + + // B와 C타입만 올 수 있음 + // E타입만 올 수 있음 + // A, B, C, D, E 타입이 올 수 있음 + + // B와 A타입만 올 수 있음 + // E, D, A타입만 올 수 있음 + // A타입만 올 수 있음 + + // B와 A타입만 올 수 있음 + // E, D, A타입만 올 수 있음 + // A타입만 올 수 있음 +``` +주석에 적혀있듯이 extends 키워드는 extends 뒤에 오는 타입을 최상위 타입으로 한정짓는 것이다. 계산기 예시와 같이 숫자와 관련된 클래스만 타입으로 받고싶은 경우 Number class를 상속받는 숫자형 래퍼 클래스들로만 지정하고 싶은 경우 아래와 같이 쓸 수 있다. +```java +public class ClassName { ... } +``` +super 키워드는 super 뒤에 오는 타입이 최하위 타입으로 한정된다. 대표적으로는 해당 객체가 업캐스팅(Up Casting)이 될 필요가 있을 때 사용한다. + +예로들어 '과일'이라는 클래스가 있고 이 클래스를 각각 상속받는 '사과'클래스와 '딸기'클래스가 있다고 가정해보자. + +이 때 각각의 사과와 딸기는 종류가 다르지만, 둘 다 '과일'로 보고 자료를 조작해야 할 수도 있다. (예로 들면 과일 목록을 뽑는다거나 등등..) 그럴 때 '사과'를 '과일'로 캐스팅 해야 하는데, 과일이 상위 타입이므로 업캐스팅을 해야한다. 이럴 때 쓸 수 있는 것이 바로 super이다. + +### <? extends E>와 <K extends E> 차이 +위 주석에서 보다시피 ``````와 ``````가 타입을 한정하는 범위는 똑같은데 어떤 차이가 있는지 궁금할 수 있다. 차이점은 K는 특정 타입으로 지정이 되지만, ?는 타입이 지정되지 않는다는 의미다. 즉, K extends E는 타입 지정이 필요한 제네릭 클래스를 선언할때 사용하고 ? extends E는 이미 만들어진 제네릭 클래스를 매개변수로 받거나 반환타입을 받을때 사용한다고 보면 된다. + +### 재귀적 타입 한정 +재귀적 타입 한정이란 자기 자신이 들어간 표현식을 사용하여 타입 매개변수의 허용 범위를 한정 시키는 것을 말한다. 실무에선 주로 Comparable 인터페이스와 함께 쓰인다. + +예를 들어, 다음과 같이 ```>``` 제네릭 E의 타입 범위를 Comparable 로 한정한다는 E를 중첩시킨 표현식을 사용할수 있는데, 이 말은 '타입 E는 자기 자신을 타입으로 구현한 Comparable 구현체로 한정' 한다는 뜻이다. +```java +public class ClassName > { ... } + +public class Student implements Comparable { + @Override + public int compareTo(Student s) { ... }; +} + +public class Main { + public static void main(String[] args) { + ClassName a = new ClassName(); + } +} +``` +위 예시로 보면 a 인스턴스의 E는 Student로 설정되었다. E가 Comparable을 extends 하므로 E는 Comparable를 구현한 타입이 와야 한다. 그래서 Student는 Comparable을 implements하면서 compareTo 메서드를 @Override하고 있다. 동시에 Comparable의 타입이 Student로 정해지므로 메서드 내에서 Student 타입을 활용하고 있다. + +개발자가 Student 객체끼리만 비교하고자 하는 의도를 갖고 있을때 위 예시처럼 선언할 수 있다. + +> Comparable는 객체끼리 비교를 해야 할때 compareTo() 메서드를 오버라이딩할때 구현하는 인터페이스이다.자바에서 Integer, Double, String 등이 값 비교가 되는 이유가 기본적으로 Comparable를 구현하고 있기 때문이다. + +```java +public class ClassName > { ... } +``` +그런데 위와 같은 형태도 자주 보게 될 것이다. 대표적인 예시로 Collections의 sort 메서드가 위와 같은 타입 한정을 사용한다. 왜 ```Comparable``` 가 아닌 ```Comparable``` 일까? 아래의 예시를 보자. +```java +//public class ClassName > { ... } // Error가능성 있음 +public class ClassName > { ... } // 안전성이 높음 + +public class Person {...} + +public class Student extends Person implements Comparable { + @Override + public int compareTo(Person o) { ... }; +} + +public class Main { + public static void main(String[] args) { + SaltClass a = new SaltClass(); + } +} +``` +이제는 개발자가 Student 객체끼리 뿐만 아니라 상위객체와도 비교하고자 하는 의도가 생겼다. + +예시를 보면, Student 클래스는 Person을 상속하고 ```Comparable```을 구현하고 있다. 만약 ClassName을 ```>```로 선언한다면 ```ClassName```는 ```Comparable```만을 구현해야 하는데 ```Comparable```을 구현하고 있으므로 컴파일 에러를 일으킨다. + +```>```로 선언해야 더 유연한 제약을 제공한다. 이 부분은 E 또는 E의 슈퍼클래스가 Comparable을 구현하도록 요구하므로, ```ClassName```가 ```Comparable```을 구현하는 것이 가능하다. 또는, Person이 ```Comparable```을 구현하더라도 Student는 Person을 상속받기 때문에 Student 객체끼리 뿐만 아니라 상위객체와도 비교가 가능하다. + +즉, ```>``` 는 쉽게 말하자면 E 타입 또는 E 타입의 슈퍼클래스가 Comparable을 의무적으로 구현해야한다는 뜻으로 슈퍼 클래스 타입으로 UpCasting하고자 할 때 타입 안정성을 보장받을 수 있다. + + + + + + + + +> 참고 출처 : +> +> https://st-lab.tistory.com/153 [st-lab:티스토리] +> +> https://inpa.tistory.com/entry/JAVA-☕-제네릭Generics-개념-문법-정복하기 [Inpa Dev 👨‍💻:티스토리] diff --git a/_posts/2024-02-02-JVM.md b/_posts/2024-02-02-JVM.md new file mode 100644 index 0000000..9def99c --- /dev/null +++ b/_posts/2024-02-02-JVM.md @@ -0,0 +1,174 @@ +--- +layout: post +title: JVM +author: 정지용 +categories: 기술세미나 +banner: + image: https://github.com/Kernel360/blog-image/blob/main/2024/0202/header.jpg?raw=true + + background: "#000" + height: "100vh" + min_height: "38vh" + heading_style: "font-size: 4.25em; font-weight: bold; text-decoration: underline" + tags: [JVM, JAVA, Jit, Jrockit, WORA, 기술세미나] +--- + + +# JVM + +반갑습니다. +지난시간 RDBMS에 이어 이번에는 JVM을 주제로 기술세미나 발표를 한 정지용 입니다. + +## [1. 개요] + +우리가 자바를 공부하고, 스프링을 쓰고 있지만 + +기업의 니즈 때문에 라는 이유를 빼고, 왜 자바를 쓰는지 생각해보신적 있으신가요? + +제가 생각하는 자바를 쓰는 이유는 객체지향이라는 개념적인 부분도 있겠지만 + +우리가 도커를 사용하듯 OS에 종속이 최소인 기술적 장점도 있기 때문이라 생각합니다. + +이번 시간에는 JVM이란 무엇인지 일부 알아보는 시간을 가져보고자 합니다. + +
+ +## [2. JVM란?] + +![image](https://github.com/Kernel360/blog-image/blob/main/2024/0202/1.png?raw=true) + +JVM은 이름 그대로 자바를 가상의 환경에서 작동시켜주는 소프트웨어입니다. + +WORA 원칙을 가지고 있어 한번 작성하면 어디서든 동일하게 실행 할 수 있도록 설계되었습니다. + +JVM의 요소는 아래와 같은 항목들이 존재합니다. + +이 항목들을 10분이라는 짧은 시간동안 전부 알기엔 부족하므로, 초록색 글씨의 2가지만 살펴보고자 합니다. + +
+ +## [3. C vs JAVA] + +![image](https://github.com/Kernel360/blog-image/blob/main/2024/0202/2.png?raw=true) + +이 과정을 알기 위해선 불가피하게 C와 JAVA의 컴파일 과정 차이를 알아야 합니다. + +C언어는 이미지의 왼쪽처럼 다양한 과정을 통해 결국 기계어로 변환하여 CPU의 명령어 세트에 맞춰지고, + +이로 인해 다른 CPU에서 동일한 코드를 실행하려면 그에 맞는 재컴파일을 해야합니다. + +이번에 맥OS 환경에서 구축 된 도커 이미지를 vultr에 도커 컨테이너로 올려보셨다면 + +아키텍쳐가 달라서 실행이 불가하다는 이슈를 접하셨을텐데, 매우 유사한 시나리오 입니다. + +자바는 그에 비해 우리가 작성한 소스 코드를 바이트 코드로 변환합니다. + +JVM은 이 바이트 코드를 OS에 맞는 네이티브 코드로 변환하고 실행합니다. + +이 역할을 해주는 담당자가 JVM의 요소중 누구냐면 바로 다음장에 나오는 Jit Compiler 입니다. + +
+ +## [4. Jit Compiler] + +![image](https://github.com/Kernel360/blog-image/blob/main/2024/0202/3.png?raw=true) + +오른쪽 이미지를 보시면 HotSpot VM 라는 아키텍처 이미지가 있는데, + +간단히 말하자면 Hotspot VM은 JVM을 실행하는 런처와 스레드 관리, JNI 관리 등을 하는 곳이고 + +테트리스 맞추듯 GC와 컴파일러를 끼워맞춰 사용합니다. + +C 컴파일러는 한번 컴파일을 요구하면 바이트 코드까지 한번에 만들어 버리는데, + +자바는 실제 애플리케이션 실행에 필요한 네이티브 코드를 만드는 책임을 JVM에게 넘겼습니다. + +그렇기 때문에 JVM은 항상 바이트 코드로 시작하여 동적으로 네이티브 코드로 바뀝니다. + +모든 코드는 이미지에 보이는 인터프리터에 의해 어떤 코드들이 있는지 인지는 되지만 + +실질적인 컴파일은 해당 코드가 많이 사용되는 우선순위에 따라 결정됩니다. + +그 결정을 담당하는건 옵티마이저가 하고, + +수행카운터와 백에지카운터라는 두개의 카운터에 의해 우선순위가 결정됩니다. + +쉽게 풀어서 말하자면 수행카운터는 메서드가 시작 할 때마다 증가하는 값이고 + +백에지 카운터는 메서드가 얼마나 반복적으로 수행되는지를 체크하는 값입니다. + +그렇게 결정된 우선순위에 따라 큐에 컴파일요청을 쌓게되고, 컴파일러는 순서대로 컴파일을 합니다. + +JVM 옵션에서 백에지 카운터 수치를 조정 할 순 있지만 + +VM튜닝을 하는 것이 아닌이상 보편적으로 손댈일은 잘 없지않을까 싶습니다. + +이 덕분에 자바의 JVM은 한번 작성한 코드를 어디서든 실행 할 수 있는 WORA를 준수하게 됩니다. + + +
+ +## [5. JVM의 종류] + +![image](https://github.com/Kernel360/blog-image/blob/main/2024/0202/4.png?raw=true) + +이제 JIT 컴파일러의 최적화 과정을 말씀드릴까 하는데 + +이 JVM이라는 것도 목적에 따라 종류가 매우 다양합니다. + +첫번째 이 Jrockit은 밑의 핫스팟이 쓰이기전에 나온 초기버전입니다. + +두번째 핫스팟 JVM은 요즘 자바에서 쓰이는 표준 JVM이라 이해하시면 되겠습니다. + +세번째는 IBM에서 개발한 것으로 경량화되어 임베디드나 클라우드 환경에서 쓰이고 이클립스 재단에서 관리하고 있습니다. + +그랄VM은 자바뿐만 아니라 자바스크립트, 파이썬, 루비처럼 다른 언어도 지원하는 것이 특징입니다. + +중국어처럼 보이는 아줄 징 JVM은 놀랍게도 미국회사이고, GC 성능이 강조된 고성능 JVM인 것이 특징입니다. + +네이버 클라우드 플랫폼도 이 아줄 JVm을 사용 및 서비스합니다. + +종류가 이만큼 많다는건? 내부 구조도 다 다르다는 이야기입니다. + +우리는 그중 첫번째 J락킷의 컴파일러로 알아보려고 합니다. + +## [6. Jrockit의 최적화 - 구조] + +![image](https://github.com/Kernel360/blog-image/blob/main/2024/0202/5.png?raw=true) + +첫 부팅때는 모든 메서드에 대해 최초 컴파일을 거친다면 오버헤드로 인해 느릴 수 있지만 + +앞서 설명한 JIT 옵티마이저, 컴파일러로 인해 시작은 느려도 판단에 따라 최적화가 이뤄지므로 + +지속적으로 수행 할 수록 더 빠른 처리가 이루어집니다. + +두번째 스레드 모니터는 샘플러스레드가 어떤 스레드들이 동작중인지, 수행내역을 관리합니다. + +이를 통해 어떤 메서드가 많이 쓰이는가 최적화 대상을 탐색합니다. + +마지막으로 샘플러 스레드가 식별한 대상을 최적화하며 + +수행중인 어플리케이션에 영향을 주지 않습니다. + +## [6. Jrockit의 최적화 - 코드] + +![image](https://github.com/Kernel360/blog-image/blob/main/2024/0202/6.png?raw=true) + +첫번째와 같이 Class A에서 Class B를 사용하고 void foo()를 통해 y, z에 b.get()을 대입하여 sum을 하는 코드가 있다고 가정해봅시다. + +1. final method의 inline 처리를 합니다. b.get()을 b.value로 바꾸어 메서드콜로 인한 성능저하를 개선합니다. +2. z,y 값이 동일하므로 z에 y를 할당하여 불필요한 호출 부하를 제거합니다. +3. z와 y가 동일하므로 불필요 변수 z를 y로 변경합니다. +4. 3의 과정으로 y=y가 되었으므로 데드코드를 삭제합니다. + +이러한 최적화 과정을 통해 우리가 코드를 작성하면서 가독성을 살리기위해 + +같은 변수를 여러번 사용, 대입하여도 성능에 지장을 최소화 하도록 하게됩니다. + +마치 우리가 코드 리팩토링을 하는 과정과 비슷하죠? + +이렇게해서 JVM의 OWRA의 의미가 무엇인지, 바이트코드는 어떤 과정으로 컴파일 되는지 + +컴파일에 쓰이는 많은 JVM중 가장 기초적인 Jrockit의 최적화 과정을 살펴보았습니다. 감사합니다. + + diff --git a/_posts/2024-02-05-DNS.md b/_posts/2024-02-05-DNS.md new file mode 100644 index 0000000..b435e79 --- /dev/null +++ b/_posts/2024-02-05-DNS.md @@ -0,0 +1,155 @@ +--- +layout: post +title: DNS 흐름 파악하기 +author: 이윤선 +categories: 기술세미나 +banner: + image: https://raw.githubusercontent.com/Kernel360/blog-image/main/2024/0205/5.png + background: "#000" + height: "100vh" + min_height: "38vh" + heading_style: "font-size: 4.25em; font-weight: bold; text-decoration: underline" +tags: [DNS, 기술세미나] +--- + +## 목차 + +1. 도메인과 호스트네임 +2. DNS란 +3. DNS 동작 과정 +4. DNS 레코드 타입 +5. 최종 정리 + +--- + +# 1. 도메인과 호스트네임 + +[www.naver.com](http://www.naver.com) 이라는 URL이 있을 때 도메인은 어느부분을 말하는 것일까요? + +www를 제외한 naver.com이 실제 도메인입니다. + +그럼 우리가 사용했었던 www는 무엇일까요? + +www는 호스트네임 입니다.  www 말고도 여러가지가 도메인 앞에 .과 함께 붙게되는데 + +예를들자면 + +### map.naver.com +![1](https://github.com/Kernel360/blog-image/blob/main/2024/0205/1.png?raw=true) + +### news.naver.com +![2](https://github.com/Kernel360/blog-image/blob/main/2024/0205/2.png?raw=true) +이라면 news, map은 호스트 네임 입니다. + +이제 실제로 DNS가 무엇이고 어떻게 동작하는지에 대해 네이버 웹사이트에 접속하는 것을 예로 들어보겠습니다. + + + +우리가 네이버 접속하러면 네이버가 제공하는 서버에 요청을 해서 데이터들을 받아올라면 네이버 서버의 IP 주소를 알아야 합니다. + +하지만 사용자가 각 서비스들의 서버 IP주소들을 다 외울 수는 없으니까 도메인을 통해서 해당 서버로 요청을 하게 되는데, + +그럼 네이버 서버의 IP가 naver.com이라는 도메인과 매핑되어 있는 정보는 어디에 있을까요? + +이러한 정보들이 저장되는 곳이 **DNS** 입니다. + +# 2. DNS란 + +# DNS(Domain Name System) + +- 도메인 이름과 IP 주소에 대한 정보를 관리하는 시스템 +- 각 서버의 IP주소를 의미있는 문자열인 도메인 추상화 +- 도메인만 IP주소를 몰라도 서버에 요청을 할 수 있음 + +DNS란 Domain Name System의 약자로 이름에서 알 수 있듯 도메인과 관련된 시스템입니다. + +DNS가 도메인 이름과 IP 주소에 대한 정보를 관리하고 있기 때문에 사용자들을 각 서버의 IP 주소를 몰라도 도메인을 브라우저에 입력해서 서버에 쉽게 접속할 수 있었던 것 입니다. + +# 3. DNS 동작과정 + +DNS는 다층적으로 구성된 네트워크에 분산되서 도메인 정보들이 저장되고 조회됩니다. +![4](https://github.com/Kernel360/blog-image/blob/main/2024/0205/4.png?raw=true) +DNS 서버는 네임서버라고 불리기도 하는데, 위 사진에서의 NS를, DNS 서버로 라고 생각하시면 됩니다. + +위 사진과 함께 DNS의 동작과정에 대해서 살펴보겠습니다. + +처음에 브라우저는 브라우저 캐시에서 요청한 도메인의 IP를 갖고 있는지 확인합니다. + +없다면 hosts 파일과 캐시에서도 IP를 확인하고 여기에도 없다면 로컬 DNS서버 캐시를 확인합니다. + +또 없다면 로컬 DNS는 Root DNS서버에다가 www.naver.com에 주소에 해당하는 IP를 어디서 찾을 수 있는지 물어봅니다.(루트 DNS 서버는 전 세계에 퍼져 있으며 한국에는 없지만 미러 서버가 이를 대신 합니다.) + +이 루트 DNS 서버는 응답으로 .com 으로 끝나는 도메인들을 담당하는 서버의 IP주소를 반환합니다. + +로컬 DNS 서버는 이 주소를 받고 최상위 도메인 서버로 찾아갑니다. + +최상위 도메인 서버는 naver.com 도메인 정보를 가진 DNS 서버 IP 주소를 반환하고 + +그 주소를 보고 마지막으로 sub domain 서버를 찾아가면 naver.com 여러 호스트네임별 IP주소가 있습니다. + +여기로부터 www.naver.com의 IP 주소를 얻어서 비로소 www.naver.com의 서버로 접속하게 되는겁니다. + +# 3. DNS 레코드 타입 + +- 도메인 이름에 어떤 타입의 값이 매핑되는지로 나뉨 +- A, CNAME, NS, SOA등.. + + + +DNS 레코드 타입을 통해 도메인 이름에 어떤 타입의 값으로 매핑될지 설정할 수 있으며 A, CNAME, SOA, NS 등이 있습니다. + +여기서 대표적인 A, CNAME, NS 레코드를 살펴보겠습니다. + +## DNS 레코드 타입 : A + +- A 타입은 도메인 IP주소를 매핑해준다는 의미 +- 해당 도메인으로 들어오면 IP주소로 이동 + +## DNS 레코드 타입 : CNAME + +- 도메인 이름에 대한 별칭을 매핑 +- A 타입을 이용해서 IP 주소를 명시하여 바로 이동할 수 있지만 CNAME을 사용하는 이유는 만약 서버 주소가 변경될 일이 있으면 별칭으로 매핑되어 있기 때문에 따로 값을 변경해주지 않아도 되기 때문입니다. 따라서 CNAME을 이용하면 변경에 유연한 구조로 가져갈 수 있다 라는 장점을 가집니다. + +## DNS 레코드 타입 : NS + +- 도메인 이름에 대한 권한이 있는 네임 서버를 매핑 +- 다른 네임 서버에 도메인 이름에 대한 권한을 위임 + +# 4. DNS 동작과정 최종정리 +![6](https://github.com/Kernel360/blog-image/blob/main/2024/0205/6.png?raw=true) +이제 마지막으로 DNS 동작과정을 정리해보겠습니다. + +브라우저에서 로컬 DNS 서버로 www.jdon.kr 요청을 보냅니다. + +로컬 DNS 서버에서 캐시를 확인하고 Root로 갑니다. + +그런데 Root에는 A 값이 없습니다. 아이피 주소가 없다는 것입니다. + +그래서 NS 값이 kr 네임 서버의 주소를 응답하게 됩니다. + +그러면 여기서 서비스의 네임서버의 주소를 응답해줍니다. + +네임 서버에 매핑된 cname으로 jdon.kr.이 매핑되어 있습니다. + +그래서 네임서버 내부에서 요청을 해가지고 이 jdon.kr에 대한 A 타입인 IP를 찾아서 최종적으로 클라이언트에게 주게 되는 것 입니다. + + + + + + + + + + + + + + + + + + + + + diff --git a/_posts/2024-02-06-Pinpoint.md b/_posts/2024-02-06-Pinpoint.md new file mode 100644 index 0000000..b8700a9 --- /dev/null +++ b/_posts/2024-02-06-Pinpoint.md @@ -0,0 +1,221 @@ +--- +layout: post +title: APM(feat.Pinpoint) +author: 홍주광 +categories: 기술세미나 +banner: + image: https://raw.githubusercontent.com/Kernel360/blog-image/main/2024/0206/2_pinpoint.png + background: "#000" + height: "100vh" + min_height: "38vh" + heading_style: "font-size: 4.25em; font-weight: bold; text-decoration: underline" +tags: [APM, Pinpoint, LGTM, 모니터링] +--- + +안녕하세요. APM 그리고 핀포인트에 대해 발표하게 된 홍주광입니다. + +## LGTM + +여러분 LGTM 이라는 말을 들어보셨나요? + +![1](https://raw.githubusercontent.com/Kernel360/blog-image/main/2024/0206/1_lgtm.png) + +코드리뷰 때 들어보셨을 것 같은데요. 제가 소개드릴 LGTM 은 코드리뷰 때가 아닌 + +그라파나에서 밀고 있는 기술스택을 의미합니다! + +> L : Limit(로그 수집 저장소) +> +> G : Grafana(시각화) +> +> T : Tempo(분산 추적 저장소, APM) +> +> M : Mimir(시계열 메트릭 저장소) + +## LGTM을 미는 이유 + +그렇다면 그라파나에서는 왜 LGTM 을 밀고 있을까요? + +보통 시스템 모니터링, 로깅, 애플리케이션 성능 모니터링을 구성하기 위해 대부분의 회사에서는 여러가지 오픈소스와 SaaS를 구성하여 사용하고 있습니다. + +시스템 모니터링 +* 프로메테우스, 그라파나, Zabbix, DataDog 등 + +APM +* Scouter, Elastic APM, Pinpoint 등 + +로깅 +* ELK, Loki, CloudWatch 등 + +위처럼 정말 여러가지 오픈소스와 SaaS 가 있습니다. + +CPU, Memory, Disk, Network Bytes 등의 리소스를 확인하기 위해서는 A 에서 확인하고, + +Application Log 를 확인하기 위해서는 B 에서 확인하고 + +APM 을 확인하기 위해서는 C 에서 확인해야 하는 상황이 생깁니다. + +이를 해결하기 위해 그라파나에서는 단일 대시보드에서 시스템 메트릭과 로그, 분산 트레이싱을 모두 확인할 수 있도록 + +LGTM(Loki, Grafana, Tempo, Mimir) 를 밀고 있습니다. + +## APM + +APM 은 + +> Application Performance Management +> +> Application Performance Monitoring + +입니다. 애플리케이션의 성능을 관리, 모니터링을 뜻합니다. + +APM 도구를 사용하면 서버에서 발생하는 메트릭(CPU, Memory, Thread, Transaction, ...), 이벤트, 로그, 트랜잭션 등을 모니터링할 수 있습니다. + +## APM 주요기능 + +* 성능 문제를 예측하고 방지 + +* 고객 기대 성능 보장, 고객 경험 향상 + +* 응답 시간 보장 + +* 가용성 증대, 다운타임 감소 + +## APM 도구의 핵심 지표 + +* 응답시간 + +* 요청 비율 + +* 에러율 + +* 리소스 사용률 + +* 운영중인 시스템 수 + +## 핀포인트 + +![2](https://raw.githubusercontent.com/Kernel360/blog-image/main/2024/0206/2_pinpoint.png) + +핀포인트는 대규모 분산 시스템, 특히 Java로 구축된 시스템의 성능을 모니터링하고 분석하도록 설계된 + +오픈 소스 애플리케이션 성능 관리(APM) 도구 입니다. + +## 핀포인트 특징 + +![3](https://raw.githubusercontent.com/Kernel360/blog-image/main/2024/0206/3_핀포인트특징.png) + +이 중에서 가장 장점으로 꼽히는 것은 아무래도 + +`Bytecode instrumentation` 입니다. 즉, 코드 수정없이 없는 것이죠. + +그 외에도 여러가지 특징이 있으니 한번 읽어보시면 좋을 것 같습니다. + +## 프로메테우스랑 그라파나가 있는데 굳이 써야할까? + +보통 프로젝트를 하시다보면 프로메테우스랑 그라파나로 모니터링을 설정하신 팀들이 많으실 텐데요. + +저희 팀 또한 그랬고 추가적인 모니터링 도구가 필요할까에 대한 의문을 가지고 있었습니다. + +결론부터 말씀드리자면 하는 모니터링 역할이 달라서 추가해도 좋다인데요. + +지금부터 핀포인트로 그 이유를 설명해 드리겠습니다. + +1. 핀포인트는 트레이싱을 통해 요청을 추적할 수 있고, 어느 지점에서 문제가 되었는지 정확히 파악하고 알려줍니다. +2. 데이터 수집 방식에 따른 확장성 차이 +- 프로메테우스는 Exporter 가 주기적으로 메트릭 데이터를 Pull 방식으로 수집하지만 핀포인트는 Push 방식으로 Collector 에 전달합니다. + + -> 이는 곧 프로메테우스보다 핀포인트가 분산 서버에 적합하다는 뜻이기도 합니다. + +## 핀포인트를 알아보자 + +![4](https://raw.githubusercontent.com/Kernel360/blog-image/main/2024/0206/4_핀포인트사진1.png) + +위에 보이는 사진은 ServerMap 입니다. + +ServerMap 지표를 활용하면 서버의 전체적인 시스템 아키텍처와, 각 End-point에 대한 요청 비율 등을 쉽게 파악할 수 있습니다. + +1번을 보시면 시스템 전체 구조를 파악할 수 있고 어디로 요청이 얼만큼 가는지, 평균 응답속도는 얼마인지를 알 수 있습니다. + +2번을 보시면 그래프로 지표를 볼 수 있고 2번 박스의 위쪽 그래프의 점들은 하나의 트랜잭션을 의미합니다. + +* 분산 시스템의 연결된 상황과 트랜잭션을 시각화 + +* 각 요청의 성공, 실패 지표 제공 + +* 각 end-point에 대한 요청 수 제공 + +* 각 요청의 응답 시간 제공 + +![5](https://raw.githubusercontent.com/Kernel360/blog-image/main/2024/0206/5_핀포인트사진2.png) + +위 사진은 멀티모듈 프로젝트의 핀포인트 사용 사례 사진입니다. + + +![6](https://raw.githubusercontent.com/Kernel360/blog-image/main/2024/0206/6_핀포인트사진3.png) + +핀포인트도 그라파나처럼 힙 사용량과 CPU 사용량도 같이 제공하고 있습니다. + + +![7](https://raw.githubusercontent.com/Kernel360/blog-image/main/2024/0206/7_핀포인트사진4.png) + +위 사진은 부하테스트(스트레스 테스트)를 진행한 모습인데요. + +점점 응답 시간이 오래 걸리다가 빨간 점들이 생기면서 Fail 이 나고 있는 모습입니다. + +이처럼 실패가 났을 때 추적하는 방법을 말씀드리겠습니다. + +![8](https://raw.githubusercontent.com/Kernel360/blog-image/main/2024/0206/8_핀포인트사진5.png) + +![9](https://raw.githubusercontent.com/Kernel360/blog-image/main/2024/0206/9_핀포인트사진6.png) + +빨간 박스만 따라가면서 보시면 되는데요. + +첫번째 사진에서 드래그라고 적힌 박스처럼 빨간 점이 생긴 부분을 드래그하면 아래 사진처럼 트랜잭션이 나오게 됩니다. + +Call Tree 로 되어있어서 타고 들어가면 어떤 사유로 에러가 발생했는지 추적이 가능합니다. 또한 응답속도와 정확히 어느 지점에서 에러가 났는지 알 수 있습니다. + +## 핀포인트 설치 + +핀포인트 설치는 정말 간단한데요. + +![11](https://raw.githubusercontent.com/Kernel360/blog-image/main/2024/0206/11_설치1.png) + +* 터미널에서 단 4줄만 입력하시면 설치가 끝납니다. + +![12](https://raw.githubusercontent.com/Kernel360/blog-image/main/2024/0206/12_설치2.png) + +* 프로젝트를 여시고 빨간 박스부분에 아래처럼 입력하시면 됩니다. + +![14](https://raw.githubusercontent.com/Kernel360/blog-image/main/2024/0206/14_설치4.png) + + +![13](https://raw.githubusercontent.com/Kernel360/blog-image/main/2024/0206/13_설치3.png) + +![16](https://raw.githubusercontent.com/Kernel360/blog-image/main/2024/0206/15_설치5.png) + +설정이 완료되고 프로젝트를 실행하시면 평소 뜨던 SPRING 글자보다 PINPOINT 글자가 먼저 나오는 걸 볼 수 있습니다. + +그러면 성공입니다. + +## 핀포인트 도입 팁 + +저희 팀도 프로젝트에 핀포인트를 사용하려고 백엔드 서버에 설치를 했는데요. 생각보다 많은 메모리를 사용하는 것을 확인했습니다. + +당시, 백엔드 서버는 t2.micro EC2 였으며 도커로 핀포인트를 돌렸을 때 메모리가 7~8GB 나왔었습니다. 그래서 서버가 죽는 현상이 발생했습니다. + +서버 스펙을 올리기에는 배보다 배꼽이 큰 상황이여서 다른 방법을 찾아보았습니다. + +![15](https://raw.githubusercontent.com/Kernel360/blog-image/main/2024/0206/15_구조.png) + +위 사진은 분산 서버시에 핀포인트 적용한 구조인데요. 위에 보시다 싶이 서버 옆에는 Agent 만 두고 나머지는 다른 별도의 서버에 두는 방식입니다. + +핀포인트 서버를 별도로 만들어 혼자 사용하거나, 혹은 로컬에서 돌리는 방법을 찾게되었습니다. + +## 결론 + +그라파나가 숲을 본다면 APM은 나무를 본다고 말 할 수 있습니다. 어느 지점에서 실패가 났고 어느 지점에 병목 현상이 일어났는 지 APM 도구를 사용한다면 더 빠르게 대처하고 + +성능 개선에 도움이 될 것 이라고 생각됩니다. + +여러분도 관심있으시다면 한 번쯤 도입해보는 것을 추천합니다. diff --git a/_posts/2024-02-08-dynamicprogramming.md b/_posts/2024-02-08-dynamicprogramming.md new file mode 100644 index 0000000..a5b695c --- /dev/null +++ b/_posts/2024-02-08-dynamicprogramming.md @@ -0,0 +1,213 @@ +--- +layout: post +title: 다이나믹 프로그래밍 +author: 김원상 +categories: 기술세미나 +banner: + image: https://github.com/Kernel360/blog-image/blob/main/2024/0208/1.png?raw=true + background: "#000" + height: "100vh" + min_height: "38vh" + heading_style: "font-size: 4.25em; font-weight: bold; text-decoration: underline" + tags: [알고리즘, 다이나믹프로그래밍] +--- + +안녕하세요. 이번에 다이나믹 프로그래밍에 관하여 기술세미나를 드릴 김원상입니다. + +다이나믹 프로그래밍은 코딩테스트를 처음 접하시는 분들에게는 진입장벽, 조금 익숙하신 분들에게는 알다가도 모를것 같은 알고리즙인데요. 제 나름대로 다이나믹 프로그래밍 문제를 접근하는 방법과 어떤 식으로 공부하면 좋을지에 대한 생각을 나눌 수 있을 것 같아 선택한 주제입니다. 커널360 코딩테스트에도 나왔고, 다른 기업 코딩테스트 후기를 봐도 조금씩 나오기 때문에 연습을 많이 해두시면 분명 원하는 회사를 가시는데 도움이 될 거라 생각합니다. + +### 1. 리처드 E. 벨만 + +다이나믹 프로그래밍은 리처드 E. 벨만이라는 수학자가 처음 고안한 알고리즘입니다. 벨만에 따르면 일련의 결정은 정책(Policy)을 구성합니다. 그리고 정책은 결과값으로 이익 또는 비용(Cost)를 제공하게 되는데요. 가장 높은 이익 또는 가장 낮은 비용을 제공하는 정책을 최적 정책(Optimal Policy)라고 합니다. 아래 세가지는 최적 정책에 관하여 가장 중요하다 싶은 부분을 최초로 다이나믹 프로그래밍이 발표된 논문에서 발췌하여 요약한 것입니다. + +- 최적 정책을 구할 때 모든 가능한 정책을 구하는 것은 보통 실용적(Practical)하지 않다. +- 어느 시점에서 어떤 행동을 선택할지에 대한 일반적인 처방(Prescription)만 있으면 최적 정책을 구하기에 충분하다. +- 다이나믹 프로그래밍의 기본적인 아이디어(Principle of Optimality) : 초기 상태 또는 선택이 무엇이든 간에 그에 따르는 선택은 반드시 최적 정책을 구성해야한다. + +### 2. 다이나믹 프로그래밍의 두 가지 분류 + +- 결정론적이냐 확률론적이냐 그것이 문제로다! + +위 최적 정책에 대한 설명 이외에 다이나믹 프로그래밍에서 주목할 부분은 결정론적 알고리즘이냐 확률론적 알고리즘이냐 하는 문제가 있는데요. 보통 코딩테스트에 나오는 문제는 **결정론적** 알고리즘이라 보시면 되겠습니다. 즉, 여러번 동일한 코드를 돌려도 같은 결과가 나온다는 것인데요. 논리적으로 사고하고 코드를 직접 작성할 수 있는 능력을 보는게 코딩테스트라면 그에 맞는 문제를 내기위해선 결정론적 알고리즘을 준비하는게 필요할 것입니다. + + +### 3. 알고리즘 개론 + +앞으로는 알고리즘의 교과서라고 할 수 있는 Introduction to Algorithms의 다이나믹 프로그래밍 부분을 발췌한 것입니다. 해당 알고리즘을 풀 수 있는 기본적인 가이드라인과 구체적인 예제로 Rod Cutting을 다루어 보겠습니다. + + +### 4. 다이나믹 프로그래밍을 푸는 4단계 과정 + +이 책에 따르면 다음과 같은 4단계를 거쳐 문제를 풀 수 있다고 합니다. + +1. 최적해의 구조를 특징지음 +2. 재귀적으로 최적해의 값을 정의 +3. 계산(주로 Bottom-up 방식) +4. 최적해를 구성 + +구체적이고 대표적인 예시인 Rod Cutting문제를 통해 다음과 같은 단계를 거쳐 보겠습니다. + +#### 4-1. 최적해의 구조를 특징지음 + +Rod는 통나무 또는 철근이라고 볼 수 있습니다. 일정한 정수 길이 n을 지니며 양의 정수 길이로 자를 수 있습니다. 각 길이마다 가격이 형성되어 있는데 해당 길이를 지니는 Rod를 잘라 얻을 수 있는 가장 높은 가격의 합을 구하는 것이 Rod Cutting 문제입니다. 유관한 수식을 정리하고 가격표를 그리면 다음과 아래와 같습니다. 여기서 최적해는 r_n에서 보는 것처럼 잘린 Rod의 가격의 총합이 되겠지요. + +
수식
+ +$$ +Rod의 전체 길이 : n \\ +$$ +$$ +i 길이의 Rod 가격 : p_i \\ +$$ +$$ +주어진 n에서 Rod를 잘라 얻을 수 있는 최대 수익 : r_n = p_{i1} + p_{i2} + ... + p_{ik} \\ +$$ +$$ +\sum_{j=1}^{k} ij = n +$$ + + +가격표 +|*length i*|1|2|3|4|5|6|7|8|9|10| +|---|---|---|---|---|---|---|---|---|---|---| +|*price pi*|1|5|8|9|10|17|17|20|24|30| + + +이렇게만 봐서는 문제를 이해하기 힘들 수 있습니다. 그럼 구체적인 예시를 들어볼까요? + +아래 그림에 n이 4인 경우 최적해를 구하는 방법이 도식화되어 있습니다 + +![1](https://github.com/Kernel360/blog-image/blob/main/2024/0208/3.png?raw=true) + +그림을 보시면 Rod를 자를 수있는 모든 경우가 표시되어있고 각 부분의 가격이 위에 적시되어있습니다. 그렇다고 보았을 때 최적해의 값은 세번째의 가격의 합인 10이 됩니다. + +하지만 이런식으로 모든 경우를 구하여 최적해를 구하는 것은 매우 비효율적이라고 볼 수 있습니다. 자세한 설명은 아래 재귀적으로 최적해의 값을 정의하는 부분에서 자세히 알아보겠습니다. + + +#### 4-2. 재귀적으로 최적해의 값을 정의 + +이 부분은 다이나믹 프로그래밍 풀이와 공부의 핵심이자, 우리가 알고리즘을 꾸준하게 공부해야하는 이유가 된다고 하겠습니다. + +왜냐하면 코딩테스트는 이 문제를 어떠한 알고리즘을 사용하여 풀 수 있다고 가르쳐주지 않기 때문이죠. 따라서 이 문제가 다이나믹 프로그래밍으로 풀 수 있는지, 그리고 어떠한 점화식으로 풀 수 있는지를 명쾌하게 풀이해내는 능력이 매우 중요합니다. Rod Cutting 문제에 대하여 최적해의 값을 정의해보겠습니다. + +n의 값이 주어져있고 그 이전의 n-1부터 1일 때의 최적해의 값을 알고있다고 가정하겠습니다. 길이가 0인 r값은 0이라는 가정하에 Rod는 n길이부터 1까지 자를 수 있으며 남은 길이는 앞서 알고 있는 최적해의 값으로 변환될 수 있습니다. 앞서 알고 있는 최적해의 값은 Optimal Substructure라고 할 수 있으며, 이를 가장 적게 활용한 방식이 아래 그림의 두번째 수식이라 할 수 있습니다. + +![2](https://github.com/Kernel360/blog-image/blob/main/2024/0208/4.png?raw=true) + +재귀적으로 최적해의 값을 정의하는 것도 성공했습니다. 그럼 이를 코드로 옮겨 봐야죠 ~ + +의사코드로 표현한다면 다음과 같은 방식으로 표현이 됩니다. + +![3](https://github.com/Kernel360/blog-image/blob/main/2024/0208/5.png?raw=true) + +이렇게 표현된 의사코드를 Python으로 작성하면 다음과 같습니다. + +```Python +def cut_rod(p, n): + if n == 0: + return 0 + q = -9999 + for i in range(1, n + 1): + q = max(q, p[i-1] + cut_rod((p, n-1))) + return q +``` + +그럼 Rod Cutting 문제를 다이나믹 프로그래밍을 사용하여 효과적으로 계산한 것이 될까요? **그렇지 않습니다!** n이 10인 경우는 그 이전의 9일 때와 계산 시간에서 유의미한 차이를 보이지 않습니다만, 25와 26일때를 상정하여 코드를 돌려보았을 때 기하급수적인 차이를 관찰할 수 있습니다. + +**소요시간** +|*n*|25|26| +|---|---|---| +|*elapsed time*|00:00:14.8|00:00:32.2| + +이유는 재귀 함수가 호출하는 수많은 서브 함수로 인해 시간복잡도가 무수히 늘어나기 때문이죠. 정확히는 2^n으로 기하급수적으로 함수호출의 개수가 늘어납니다. 몇가지 가정과 간단한 점화식으로 이를 증명해 보겠습니다. + +위의 재귀적 정의나 코드에 따르면, 호출되는 함수의 개수를 T라고 할 때 0인 시점에 호출되는 함수를 1이라하고 시점이 1씩 늘어날 때마다 다음과 같은 산식이 됩니다. + +$$ +T(0) = 1 \\ +$$ +$$ +T(n) = 1 + \sum_{j=0}^{n-1} T(j) +$$ + +이 때 우리의 목표는 T를 이전 Substructure를 사용하지 않고 n에 대한 산식으로 표현하는 것입니다. 가장 간단하게 시점 1인 경우 T 값은 다음과 같이 구할 수 있습니다. + +$$ +T(1) = 1 + T(0) = 2 +$$ + +위의 점화식과 더불어 n이 2보다 큰 상황을 가정하여 해당 점화식이 어떤 식으로 일반화 될 수 있는지 살펴보겠습니다. + +$$ +T(n) = 1 + \sum_{j=0}^{n-1} T(j) \\ +$$ +$$ +T(n-1) = 1 + \sum_{j=0}^{n-2} T(j) \\ +$$ +$$ +if n \geqq 2 +$$ + +위 두 식을 위 아래로 차감하면 다음과 같은 산식을 얻을 수 있습니다. + +$$ +T(n) - T(n-1) = T(n-1) \\ +$$ +$$ +T( +if n \geqq 2 +$$ + +따라서 T를 n에 관한 식으로 풀어쓰면 다음과 같이 기하급수적인 2의 거듭제곱 꼴로 나타납니다. + +$$ +T(n) = 2 ^ n ( n\geqq 0 ) +$$ + +직관적으로 해석해도 호출되는 함수의 양이 2배씩 증가함을 알 수 있습니다. 아래는 4를 호출할 경우 "재귀적으로만" 문제를 풀었을 때 호출되는 함수의 양입니다. + +![4](https://github.com/Kernel360/blog-image/blob/main/2024/0208/7.png?raw=true) + +어기서 1을 늘려 5를 호출하였을 때 기대되는 함수의 개수는 다음과 같이 표현이 되는데, 정확하게 노드가 2배로는 것을 확인할 수 있게됩니다. + +![5](https://github.com/Kernel360/blog-image/blob/main/2024/0208/8.png?raw=true) + +여기까지 최적해를 재귀적으로 정의하고 해를 구성한다는 것이 어떤 것인지 알아보았습니다. 여기서 재귀적으로 문제를 정의할 경우 생길 수 있는 시간복잡도 문제를 해결해야한다는 것도 알아보았습니다. 마지막으로 Bottom-up방식으로 최적해를 구할 때 얻을 수있는 시간복잡도상 이점이 무엇인지 알아보겠습니다. + + +#### 4-3. Bottom-up 방식의 계산 최적화 + + Bottom-up 방식의 계산 최적화는 캐시메모리에 순차적으로 최적해의 값을 추가함으로써 재귀함수가 반복적으로 호출되지 않도록하는 것을 의미합니다. 다음의 의사코드가 그것을 표현합니다. + +![6](https://github.com/Kernel360/blog-image/blob/main/2024/0208/9.png?raw=true) + +r은 최적해를 담아두는 배열로써 활용되고 이 중 반복문이 사용되어 이전 최적해를 불러오지만 지난번처럼 기하급수적으로 재귀함수를 호출하지는 않습니다. Python으로 작성한 코드는 다음과 같으며 앞서 보았던 25와 26으로 올라가는 n값에서 기하급수적인 시간소요는 보이지 않습니다. + + +```Python +def cut_rod(p, n): + r = [0 for _ in range(n + 1)] + for j in range(1, n + 1): + q = -9999 + for i in range(1, j + 1): + q = max(q, p[i] + r[j-i]) + r[j] = q + return r[n] +``` + +**소요시간** +|*n*|25|26| +|---|---|---| +|*elapsed time*|00:00:00|00:00:00| + +끝으로 다이나믹 프로그래밍을 풀기위해 다음 4가지 단계를 다시 한번 더 리마인드 해보겠습니다. 그리고 꾸준히 문제를 푸는 것도 잊지 맙시다. + + +1. 최적해의 구조를 특징지음 +2. 재귀적으로 최적해의 값을 정의 +3. 계산(주로 Bottom-up 방식) +4. 최적해를 구성 + + +감사합니다. +