728x90

최근에 친절한 SQL 튜닝이라는 책으로 사내 스터디를 진행하게 되었는데, 이번에는 간단하게 내용을 정리하고 조만간 깃허브 블로그로 이전 후에 다시 한 번 정리를 해야할 것 같습니다. 아직 1장이지만 걱정했던 것보단 책이 비유와 함께 설명을 쉽게 해줘서 잘 읽히고 있어 다행이네요...

SQL 처리 과정

SQL은 구조적, 집합적, 선언적 질의 언어로 사용자가 작성한 SQL은 옵티마이저를 통해 최적의 실행 계획으로 프로시저로 작성된다.

SQL 최적화

최적화 = 파싱 + 최적화 + 로우 소스 생성

  1. 파싱 : 사용자로부터 전달 받은 SQL을 SQL 파서가 파싱을 진행한다.
    • 파싱 트리 생성 : SQL 문의 개별 구성 요소 분석 및 파싱 트리 생성
    • Syntax 체크 : 문법상의 오류 확인
    • Semantic 체크 : 의미상의 오류 확인 (존재하지 않는 테이블이나 컬럼을 사용하는지 혹은 권한이 부족한지)
  2. 최적화 : 옵티마이저가 미리 수집한 시스템 및 오브젝트 통계정보를 바탕으로 실행 경로를 생성해서 비교한 후 가장 효율적인 하나를 선택
    • 쿼리를 실행하기 위한 실행 계획 후보군 선정
    • 데이터 딕셔너리에 미리 수집해둔 오브젝트 통계 및 시스템 통계 정보를 이용해 각 실행 계획의 예상 비용 산정
    • 최적 비용을 나타내는 실행 계획 선택
  3. 로우 소스 생성 : 로우 소스 생성기가 실행 경로를 실제 실행 가능한 코드 또는 프로시저 형태로 포맷팅 하는 단계

실행 계획과 비용

예상 실행 계획을 통해 옵티마이저가 선택한 실행 계획이 어떻게 작성되었는지 확인할 수 있고, 해당 경로와 비용을 근거로 적합한 테이블과 인덱스를 결정할 수 있다.

실행 비용은 쿼리 수행 시간 동안 발생할 것으로 예상 되는 I/O 횟수 및 예상 소요 시간으로 어디까지나 예상치이기 때문에 실측과는 차이가 존재한다.

옵티마이저 힌트

SQL 옵티마이저가 항상 최선의 선택을 하는 것은 아니고, 복잡한 SQL일수록 실수가 발생할 가능성이 높기 때문에 개발자가 직접 옵티마이저 힌트를 사용해 통계 정보에 담을 수 없는 데이터나 업무 특성 등을 활용해 더 효율적인 액세스 경로를 선택할 수 있다.

소프트 파싱과 하드 파싱

라이브러리 캐시 : 옵티마이저가 최적화를 통해 생성한 내부 프로시저를 캐싱해두는 공간

  1. SQL 파싱
  2. 파싱된 SQL이 라이브러리 캐시에 존재하는지 확인
  3. 존재하는 경우(cache-hit) 해당 로우 소스 재사용, 존재하지 않는 경우(cache-miss) 로우 소스 생성 과정 수행

cache-hit의 경우를 소프트 파싱, cache-miss의 경우를 하드 파싱이라고 한다.

하드 파싱의 경우 옵티마이저가 쿼리를 최적화 하기 위해 아래와 같은 정보들을 사용해 다량의 CPU 연산을 처리해야 한다.

  • 테이블, 컬럼, 인덱스 구조에 관한 정보
  • 오브젝트, 시스템 통계
  • 옵티마이저 관련 파라미터

I/O 작업에 비하면 큰 작업은 아니지만, 적은 연산은 아니기에 캐싱 전략을 사용해 효율적으로 처리한다.

'Book' 카테고리의 다른 글

[HTTP 완벽 가이드] URL과 리소스  (0) 2024.03.08
[HTTP 완벽 가이드] HTTP: 웹의 기초 - 개관  (1) 2024.03.06
728x90

들어가며

 취직 후에 알고리즘을 놓아버리고 언젠간 다시 시작해야겠다고 다짐만 하고 있었는데, 마침 코드트리를 한 달 동안 체험할 수 있는 기회가 생겨서 신청하게 되었다. 간단하게 한 달 동안 사용하면서 느꼇던 코드트리의 장단점과 다른 플랫폼과의 차이점에 대해 정리해보겠다.

 

 참고로 코드트리는 유료 서비스이며, 글에서 언급할 백준과 프로그래머스는 무료 서비스다.

 

장점

 코드트리는 크게 6가지 커리큘럼과 각 커리큘럼에 여러 개의 챕터로 세분화하여 기초 문법부터 기본적인 자료구조와 알고리즘을 거쳐 심화 단계를 차근차근 배울 수 있다.

 다른 PS 플랫폼과 마찬가지로 다양한 코딩 문제를 제공해주는데, 백준과 프로그래머스의 경우에는 개념을 따로 가르쳐주지 않고, 직접 책이나 강의를 통해 학습해야 하지만, 코드트리는 언어별로 개념과 코드를 직접 가르쳐주기 때문에 알고리즘에 처음 입문하는 사용자도 쉽게 접근할 수 있다.

 IDE처럼 높은 수준의 자동 완성까지는 안되더라도 자신이 한 번 작성한 코드들에 한해서는 자동완성도 지원해서 반복적인 타이핑을 줄일 수 있다.

 백준을 가장 많이 사용하지만 개인적으로는 프로그래머스처럼 문제와 에디터가 한 페이지에 보이는 것을 더 좋아하는데, 코드트리의 UI가 한 페이지에 모두 보이고, 크기도 자유자재로 변경가능해서 편리하게 사용하고 있다.

 언어도 다양하게 지원하기 때문에 홍대병 말기 수준의 언어를 사용하는 것이 아니라면 사이트 이용에 지장은 없다.

 

단점

 문제를 풀고 나면 다른 사람이 제출한 코드들을 보면서 더 좋은 풀이와 다양한 접근 방식들을 배울 수 있는데, 필터는 언어와 상태별로 다양하게 제공되지만, 수행 시간이나 메모리 같은 것들에 대한 정렬을 제공하지 않아 페이지를 이동하며 직접 효율적인 코드들을 찾아야 하는 것이 불편하다.

 아직 기출 문제 서비스가 삼성 SW 역량 테스트만 제공한다.

 에디터만 라이트 모드와 다크 모드 전환을 지원하고 사이트의 전체적인 UI의 다크 모드는 지원하지 않는다.

 다크 모드가 아니면 숨이 안쉬어지는 사람들은 사이트를 이용하지 못할 수도 있다...

 

마치며

 아직은 서비스 기간이 짧다보니 대표적인 PS 플랫폼인 백준과 프로그래머스에 비해 커뮤니티의 활성화와 다수의 사용자들의 다양한 풀이를 볼 수 없는 부분이 아쉽지만, 한 달 동안 사용하면서 사이트 자체의 서비스는 매우 만족스럽게 사용했다.

 

 만약에 사용하는 언어가 아직 미숙하고, 알고리즘을 처음 접하는 사람이라면 다른 플랫폼들보단 코드트리를 더 추천하고 싶다. 유료긴 하지만 집중력과 긴장감을 부여하기 위해 자체 타임 어택 느낌으로 너무 긴 기간 결제하지 않고 4 ~ 6개월 정도 해보는 것도 좋다고 생각한다. 하지만 6개월 할거면 연간 결제가 더 효율적이다

 

 추가로, 작성자처럼 기존에 알고리즘을 해보았지만 오래 쉬다 다시 시작해보려는 사람들한테도 손실되었던 알고리즘 두뇌의 재활 치료 목적으로 추천하고 싶다.

728x90

 최근에 회사에서 갑작스럽게 혼자 AWS를 사용해야 하는 업무를 맡게 되어서 당분간 AWS를 공부하게 되었는데, 이번 기회에 작년에 포기했던 SAA 자격증도 도전할 겸 제대로 학습해 보고, 업무와 개인 프로젝트에서도 실제로 다양하게 사용해 봐야겠다.

 

리전

 AWS가 글로벌 서비스를 제공하기 위해 세계 각지에 데이터 센터를 클러스터링하는 물리적 위치

리전 선택 시 주의 사항

  • 국가의 정책 (예 : 프랑스의 데이터는 프랑스에 있어야 한다.)
  • 지연 시간
  • 사용하려는 AWS 서비스가 해당 리전에 제공되는지
  • 리전 간 요금 차이 확인

 

AZ (Availbility Zones)

  • 리전마다 존재하는 논리적 데이터 센터의 각 그룹
  • 최소 3개, 최대 6개의 가용 영역이 존재하고, 보통 3개의 가용 영역이 존재한다.
  • 각각의 가용 영역은 여분의 전원, 네트워킹, 통신 기능을 갖춘 하나 또는 두 개의 데이터 센터 존재
  • 가용 영역들은 재난 발생에 대비해 서로 분리되어 있어, 한 곳에 문제가 생겨도 다른 곳은 괜찮다.

 

Edge Location

  • AWS는 40개 이상의 국가의 90개 이상의 도시에 400개 이상의 엣지 로케이션 보유 중
  • 최저 지연 시간을 통해 사용자에게 빠른 데이터 전송 가능

 

IAM (Identity and Access Management)

 AWS 권한과 리소스에 대한 액세스를 안전하게 제어할 수 있는 기능을 제공해주는 서비스

동작 과정

  1. 보안 주체(Principal) : 리소스에 대한 작업을 요청하는 엔티티(사용자, 서비스, 역할)
  2. 요청(Request) : 보안 주체가 보안 자격 증명 등을 이용해 리소스에 대한 조치나 작업을 수행하게 요청
  3. 인증(Authentication) : IAM이 보안 주체와 연결된 권한을 확인
  4. 인가(Authorization) : 인증된 보안 주체의 요청에 대해 허용/거부를 결정
  5. 작업(Action) / 연산(Operation) : 보안 주체가 요청에 대한 권한이 있는 경우 요청을 진행하도록 허용
  6. 리소스 : 요청이 승인된 경우 해당하는 리소스에서 작업 수행

 

주요 개념

  • 사용자 : 리소스에 액세스 할 수 있는 사람/애플리케이션에 대해 개별 사용자를 생성해 사용자별로 고유한 보안 자격 증명 세트를 통해 AWS 리소스에 접근 가능하다.
  • 사용자 그룹 : 사용자를 그룹으로 구성해 동일한 권한을 다수의 사용자들이 해당 권한을 상속 받게 된다.
  • 역할 : 사용자나 서비스에 일시적으로 권한을 부여하는 방법이다.
  • 정책 : 사용자, 그룹, 역할에 대한 권한을 정의하는 JSON 문서다. 작업이 하용하거나 거부되는 리소스 및 조건을 정의한다.
  • 자격 증명 공급자 : 별도의 IAM 사용자 생성 없이 회사 디렉터리의 사용자나, 소셜 미디어 계정 등의 기타 자격 증명 공급자와 연동을 지원해 중앙에서 사용자 액세스 관리가 용이하게 한다.

 

EC2

 아마존의 데이터 센터의 서버용 컴퓨터에 접속해 사용할 수 있는 클라우드 컴퓨팅 서비스로 필요에 따라 용량을 늘리거나 줄일 수 있는 탄력성, 사용한만큼의 비용만 지불하는 효율성, 인스턴스의 완전 제어 가능, 효과적이고 편리한 보안 및 네트워크, 스토리지 구성/관리가 장점이다.

 

VPC

 논리적으로 격리된 가상 네트워크 안에서 AWS의 리소스를 사용할 수 있는 서비스

장점

  • 서브넷 생성
  • VPC와 서브넷에 IP 주소 할당 (IPv4, IPv6 지원)
  • 라우팅 테이블을 사용해 서브넷/게이트웨이의 네트워크 트래픽 전달 위치 결정 가능
  • 게이트웨이를 통해 VPC를 다른 네트워크와 연결 가능
  • 엔드포인트를 사용해 게이트웨이나 NAT 장치 없이도 AWS 서비스 간의 비공개 연결 가능
  • VPC 간의 피어링 제공 (다른 계정 간 혹은 다른 리전의 VPC 간의 트래픽 라우팅도 가능)
  • 트래픽 미러링을 통해 보안 및 모니터링 향상
  • 전송 게이트웨이를 사용해 VPC, VPN의 연결 및 AWS Direct Connect 연결 간 트래픽 라우팅
  • VPC 네트워크 인터페이스로 들어오고 나오는 IP 트래픽에 대한 VPC 흐름 로그 제공
  • VPC와 VPN 연결 가능
  • 확장 용이

사용 예시

  • 클라이언트가 접근해야 하는 프론트 측을 퍼블릭 서브넷을 사용해서 운영
  • 인터넷 액세스가 필요 없는 DB와 서버 등의 백엔드 시스템은 프라이빗 서브넷을 사용해서 운영
  • 서브넷으로 다수의 리소스들을 묶어서 보안 그룹 및 네트워크 액세스 제어를 보다 간단하고 효율적으로 할 수 있음

 

728x90

 요즘 바쁜 작업이 끝나고 여유로워져서 이전부터 유독 조회가 느려 성능을 개선하고 싶었던 뷰의 실행 계획을 분석해보고 있다. 문제는 DB에 대한 지식이 적고, 해본 적이 없는 분야라 처음부터 배워가면서 분석을 해야 한다는 것인데, 당분간은 구글링과 지피티에 신세를 많이 져야겠다.

 

쿼리 실행 계획

 DB에 쿼리를 전달하면 DB는 전달 받은 쿼리를 어떤 방식으로 효율적으로 접근하고 처리할지에 대해 계획을 짜고 작업을 수행하게 된다. 하지만, DB가 혼자서 자동으로 처리하는 작업이라도 개발자가 적절한 인덱싱과 통계 작업 등을 통해 DB에게 방향을 제시해 줄 수 있다.

 

SSMS에서 실행 계획 보기

그래픽 방식의 실행 계획 분석
텍스트 방식의 실행 계획 분석

 

 SSMS에서는 크게 예상 실행 계획, 실제 실행 계획, 활성 쿼리 통계 세 가지 방식으로 실행 계획을 볼 수 있다.

 

예상 실행 계획

SET SHOWPLAN_ALL ON;
GO
쿼리문
GO
SET SHOWPLAN_ALL OFF;

 

 실제로 쿼리를 실행하지 않고 SQL Server가 내부적으로 생성한 실행 계획을 보여준다. 위의 쿼리를 

 

 쿼리를 실제로 실행하지 않기 때문에 빠르게 성능 병목 현상과 데이터 접근 방식, 연산 비용 등을 분석할 수 있지만, 실제 실행 계획에 비해 정확성이 떨어진다.

 

실제 실행 계획

SET STATISTICS PROFILE ON;
GO
쿼리문
GO
SET STATISTICS PROFILE OFF;

 

 실제로 쿼리를 실행한 뒤에 과정을 기록한 실행 계획으로, 실제 데이터와 통계를 사용한 결과를 보여줘 예상 실행 계획에 비해 느리지만 정확한 계획을 분석할 수 있다.

 

활성 쿼리 통계

 실제 실행 계획과 마찬가지로 실제로 쿼리를 실행하는 것은 같지만, 쿼리가 실행 중인 중간에 실시간으로 실행 계획을 분석할 수 있어 병목 구간을 실시간으로 파악하거나, 처리 시간이 긴 쿼리를 디버깅할 때 유용하다.

 

어떤 부분을 확인해야 할까?

 처음 실행 계획을 봤을 때 뭔지도 모르는 엄청 많은 정보들이 쏟아져서 당황했었는데, 아는 것이 많지 않으니 우선은 크게 다섯 가지만 살펴보기로 했다.

 

1. 테이블 액세스 방식

 데이터를 읽을 때 어떤 방식으로 읽는지에 대한 부분인데, 인덱스를 잘 타주고 있는지를 확인하면 된다. 대부분이 인덱스와 조건 문제이기 때문에 확인 후에 제대로 처리해줘야 한다.

Table Scan  인덱스를 타지 못해 테이블의 모든 행을 순차적으로 읽는 방식이다.

 적절한 인덱스가 없거나, 조건이 인덱스를 사용할 수 없을 때 사용된다.
Index Scan  인덱스를 타지만 조건이 부족하거나, 결과 데이터가 많은 경우 인덱스의 모든 데이터를 순차적으로 읽는 방식
Index Seek  인덱스가 효율적으로 활용되어 특정 조건에 따라 필요한 인덱스를 바로 찾아가는 방식

 

2. 조인 방식

 테이블 간의 조인을 어떤 방식으로 처리하는지에 대한 부분으로 데이터 크기와 구조에 따라 달라진다.

Nested Loop Join  테이블의 각 행에 대해 다른 테이블의 데이터를 검색하는 방식이기 때문에 작은 데이터 세트에 적합하고, 큰 데이터 세트의 경우에는 비효율적이다.
Hash Match  해시 테이블을 만들어 데이터를 결합하는 방식으로 큰 데이터 세트에 적합하지만 성능 병목이 발생할 수 있으며, 생성을 위한 추가 메모리가 필요하다.

 조인 대상 테이블의 크기가 크거나, 테이블이 인덱스로 정렬되어 있지 않은 경우 사용된다.
Merge Join  정렬된 데이터를 병합하여 결합하는 방식으로, 두 테이블이 정렬되어 있거나, 정렬 비용이 낮을 때 적합하다.

 

3. Sort

 정렬, 그룹화, 중복 제거 등의 작업을 처리하기 위해 데이터를 정렬하는 과정으로 추가적인 CPU와 메모리를 소모하기 때문에 비용이 많이 발생한다. 가능하면 정렬된 인덱스를 사용하는 것이 좋다.

 

4. Filter

 WHERE, HAVING 절의 조건을 만족하지 않는 데이터를 제거하는 과정으로, 조건이 복잡할 수록 연산 비용이 높으며, 정렬과 마찬가지로 적절한 인덱스를 사용해서 성능을 향상할 수 있다.

 

5. Cost

 각 단계에서 사용하는 CPU, 메모리, I/O 리소스 등을 기반으로 상대적인 비용을 계산한 값으로, 가장 비용이 높은 단계를 찾아 성능을 개선해야 한다.

 

주의해야 할 부분과 해결책

 대표적으로 다음과 같이 연산 비용이 높게 책정되고, 성능에 영향을 끼치는 작업들이 존재한다.

 

Table Scan

 인덱스를 타지 않고 테이블의 모든 행을 검색하기 때문에 당연히 데이터가 많은 수록 성능 저하가 발생할 수 밖에 없다. 따라서 쿼리에 필요한 컬럼에 인덱스를 추가하고, 필요하지 않은 데이터를 제거하거나 조회 범위를 줄이는 것이 좋다.

 

Index Scan

 Table Scan 보다야 좋은 방식이지만 인덱스를 제대로 사용하고 있지 못한 방식이기 때문에, 조회 범위를 줄이거나, 조건에 자주 사용되는 필터링 인덱스, 커버링 인덱스를 생성하는 방식으로 쿼리를 최적화 해줘야 한다.

 

Hash Join

 큰 데이터 세트일수록 해시 테이블 생성에는 많은 메모리가 필요하기 때문에 조인 대상 테이블의 크기를 줄이는 방향으로 쿼리를 수정하고, 조인 컬럼에 적절한 인덱스를 추가해준다.

 

Sort

 정렬 작업은 CPU와 메모리 리소스를 많이 잡아 먹기 때문에 불필요한 정렬을 제거하거나 간소화하는 것이 좋고, 정렬된 인덱스를 사용하는 것이 좋다.

 

Nested Loop Join

 모든 행에 대해 반복적으로 다른 테이블을 검색하는 무식한 방법이기 때문에 큰 데이터 세트일수록 성능 저하가 심하다. 조인 시 적합한 인덱스가 없거나 사용하지 못하고 있는 경우와 큰 테이블 간의 조인시에 사용되는 방식이니 조인 컬럼에 적합한 인덱스를 추가해주고, 큰 데이터 세트의 경우에는 Hash나 Merge 방식을 사용할 수 있도록 유도해주는 것이 좋다는데 이건 어떻게 해야할지 아직 모르겠다...

 

Key Lookup

 인덱스가 쿼리에서 필요한 데이터를 모두 포함하고 있지 않은 경우에 인덱스에서 필요한 데이터를 조회한 후에 부족한 데이터를 추가로 조회하는 작업으로, 커버링 인덱스를 추가하거나, SELECT 절에서 불필요한 컬럼을 제거한다.

 

Filter

 조건절이 인덱스를 효율적으로 활용할 수 있게 조건을 간소화 혹은 구체화하고, 필터링 조건에 적합한 인덱스를 추가한다.

 

 

마치며

 실행 계획에서 사용되는 방식들에 대해 학습한 후에 문제가 있던 쿼리의 실행 계획을 봤을 때 Hash Match와 Nested Loop, 과도한 정렬 작업 등이 넘쳐나는 것을 확인했는데, 다음 주에 출근해서 이걸 처리해야 하는게 두렵기도 하지만 해결하면 얼마나 빨라질지 설레기도 한다. 할 수 있을까...

728x90

 올해는 길다면 길고 짧다면 짧은 취준 기간을 끝내고 목표로 하던 개발자로서 첫 취업을 한 성공적인 해이다. 하지만, 취업이라는 가장 큰 목표는 달성했지만, 이후에 계획했던 목표들은 대부분 달성하지 못한 것 같아서 아쉬운 해이기도 하다.

 

6개월 간의 회사생활

 취업에 성공해 5월 말부터 첫 출근을 하게 되었다. 낯을 많이 가리는 편이라 어떻게 적응해야할지 걱정이 많았는데 좋은 팀원들을 만나 어렵지 않게 적응할 수 있었다. 취업 전에 생각했던 일과 회의가 많고, 야근이 잦은 IT 회사 생활과는 많이 다르지만, 나름대로 즐겁게 회사 생활을 하고 있다.

 

 최근에는 고도화 작업을 시작하게 되었는데, 이번 백엔드 반상회에서 민재님이 하셨던 말중에 "누가 코드를 이렇게 작성했는지 커밋 내역을 보면서 욕을 하기도 하고"라는 뉘앙스의 말이 어떤건지 이해할 수 있었다. 아쉽게도 옛날에 코드를 작성했던 사람들은 다들 파견을 가고 없고, 문서도 남아있지 않기 때문에 혼자 화내고 혼자 해결해야 했다. 현재는 미래의 누군가가 내 코드를 보며 나와 같이 의문과 스트레스를 갖지 않았으면 해서 로직도 심사숙고 하면서 작성하고 문서화도 꼼꼼히 해두고 있다. 문서화의 중요성에 대해 뼈저리게 느낄 수 있는 좋은 기회(?)였다.

 

 하지만, 대부분의 프로젝트가 파견지에서 이루어지기 때문에 본사에서는 아직 간단한 유지보수나 현재 진행 중인 고도화 작업이 전부라 12월 전까지는 정말 할 일이 없었다. 아무리 내 꿈이 일 안하고 돈 버는 것이라지만, 막상 그런 상황이 되니 나는 어떤 쓸모가 있을까라는 생각을 하면서 출근해서 멍하니 시간 보내다 퇴근하는 것을 반복하기도 했다.

 

 그래서 최근에는 개발과는 전혀 상관 없는 일을 할 수도 있고, 원하는 일을 할 수도 있겠지만, 후회를 하고 도망을 치더라도 일단은 어떤 일이든 해보고 싶어져 팀장님께 말씀드려 내년 초에 파견을 가게 되었다. 좋은 선택을 한 것인지에 대해선 아직도 확신은 없지만, 좋은 결과가 아니더라도 얻을 수 있는 것들은 존재한다 생각하기 때문에 현재에 집중하면서 살아보려 한다.

 

 6개월 동안 개발적인 경험을 많이 하지는 못해서 개발자로서의 실력이 향상되었다고 느끼지는 못하지만, 좋은 팀원들을 만나 같이 일하면서 소통이 부족하던 스스로가 많이 열리고 발전했다고 느낀다.

 

도시 상경과 첫 자취

 평생을 속초와 강릉에서만 살아와서 강원도를 벗어나본 적이 없었지만, 올해 취업을 하며 처음으로 부모님 품과 강원도를 벗어나 도시에서 첫 자취를 시작하게 되었다. 취업 직후 3개월 정도는 이모께서 잠깐 지내도 괜찮다 하셔서 얹혀 살다가 9월에 원룸을 구하게 되었는데, 첫 자취를 하며 현재까지 느낀점을 한 문장으로 표현해보자면, 부모님이 알게 모르게 해주시던 것들이 엄청나게 많다는 것이다.

 

 평소 본가에 있을 때도 요리를 종종하는 편이라 자취를 해도 밥 걱정은 안해도 될 것이라 생각했지만, 장도 직접 봐야하고, 채소는 이미 손질되어 있는 것이 아니라 직접 손질을 한 뒤에야 요리를 시작할 수 있었다. 그래도 현재는 어느 정도 익숙해져서 간단한 반찬 정도를 만들어두고 아침도 잘 챙겨먹고 다닐 수 있게 되었다.

 

 요리도 문제지만 청소도 문제다. 분명 집에서 숨만 쉬고 살아가는 것 같은데 왜이리 금방 더러워지는지, 청소를 할 때마다 항상 깨끗했던 본가를 떠올리며 어머니의 대단함을 다시 한 번 느낄 수 있었다.

 

 자신했던 것만큼의 자취를 하고 있지는 않지만, 현재까지 굶지 않고 살아 숨쉴 정도로의 자취 생활을 이뤄낸 스스로에게 고생했다는 말을 해주고 싶고, 내년에는 좀 더 다양한 요리도 해보고, 집에서의 루틴도 만들어서 지금보다는 좀 덜 칙칙한 자취 생활을 할 수 있게 노력해야겠다.

 

취업 후의 계획들

 입사 소식을 접하고 한껏 들떠 아침 일찍 출근해 커피 한잔과 알고리즘 한 문제를 풀고, 퇴근 후에 블로깅과 독서, 사이드 프로젝트 등을 하는 갓생을 꿈꾸었는데, 현실은 정시에 출근해 바로 일을 시작하고, 퇴근 후에는 간신히 저녁을 해먹은 후에 침대에 누워 유튜브를 보다 잠이나 자고 있다.

 

 다행히도 매일 알고리즘 한 문제를 푸는 것은 지켜지고 있지만, 그외의 모든 것들은 하나도 지켜지지 않고 있다. 최근에는 그나마 마음을 고쳐잡고 퇴근 후에 강의와 책을 보며 짧게라도 공부를 진행하고 있지만, 사이드 프로젝트는 어떻게 시작해야할지 감도 안잡힌다. 

 

 회사와 집이 10분 거리에 매일 정시 퇴근인 삶이라 아침과 저녁에 시간이 많음에도 불구하고 게으르게 살고 있는 스스로에 대해 반성하며 내년에는 이 회고글을 다시 봤을 때 부끄럼없는 한해를 보낼 수 있도록 노력해야겠다.

 

마치며

 마무리 글이라기보단 다짐글에 가까운데, 다음에 쓸 글 주제를 정해두지 않으면 항상 미루게 되는 것 같아서 앞으로는 최대한 글 주제를 정해두고 시작해보려 한다.

 

200억건의 데이터를 MySQL로 마이그레이션 할 때 고려했던 개념과 튜닝 방법 강의 | July - 인프런

July | 기존 MongoDB를 통해 관리하는 200억건의 데이터를 MySQL로 이전을 하면서 고려했던 개념들에 대해서 다루었습니다., 200억건의 데이터를 어떻게 마이그레이션 할까요?? 🤔저는 최근 실무에서 M

www.inflearn.com

 현재 위의 강의를 학습 중인데 다음 주 완강을 목표로 하여 해당 강의 내용을 바탕으로 글을 작성하겠다. 추가로 가능하다면 RealMySQL 책도 최대한 읽어서 작성할 수 있도록 노력하겠다.

'Diary' 카테고리의 다른 글

블로깅 돌아보기  (0) 2024.11.24
10일 동안 글자 하나 안 쓴 사람의 다짐글  (1) 2024.10.08
삶의 지도  (0) 2024.09.12
백준 근황  (0) 2024.07.27
솔브닥 마라톤 1코스 완주  (0) 2024.06.09
728x90

시작하며

 갑자기 블로깅 회고를 하게 된 이유는 최근에 글태기가 와서 극복해 보고자 글쓰기 세미나를 듣게 되었는데, 배우고 느낀 점이 많아 이러한 부분들을 글로 정리하고 싶었고, 오늘이 글제출 마감일인데 당장 글 쓸 주제가 생각이 안 났기 때문이다. 사실 오늘이 마감일인 게 가장 큰 이유다. 

 

블로깅을 왜 시작하게 되었을까?

 작년 초부터 학습한 내용들과 경험들을 글로 기록해 두기 위해 개발 블로그를 운영하게 되었다. 운영 초기에는 학습한 내용들을 그대로 정리하는 글 위주로 작성하다 보니 주제 선정이나 어떤 내용들을 적을지에 대한 고민이 크게 없었고, 일주일에 3~5개의 글을 작성하는 게 어렵지 않았다.

 

 어느 순간 블로그에 글들이 100개가 훌쩍 넘어가고, 적은 수지만 작성한 글을 봐주는 사람들이 생기기 시작하니, 뿌듯함과 성취감이 생겨서 더 열심히 글을 작성할 수 있는 동기 부여가 되었다.

 

블로그의 방향성에 대한 의문

 

 올해 초, 블로깅을 꾸준하게 하면서 글이 어느 정도 쌓이고 처음 작성한 글들과 최근에 작성한 글들을 돌아봤다. 글의 짜임새나 내용을 정리하는 부분에서 많이 발전했다는 생각은 들지만 스스로에게 이런 개념적인 정리글만 주야장천 쓰는 게 의미가 있을까 라는 의문이 문득 들었다.

 

 그래서 그저 글만 쓰는 게 아니라 스스로의 생각을 좀 더 표현할 수 있는 유의미한 글을 작성하고 싶어 국내 IT 기업들의 기술 블로그들을 참고해 가며 글을 쓰기 시작했다. 기존에 책이나 문서들을 읽어가며 그대로 적던 방식과는 다르게 스스로 생각하고 찾아보고 글을 적기 시작하니 글 작성에 소요되는 시간부터 차이를 느낄 수 있었고, 전달하고자 하는 내용도 더 잘 정리해서 글로 작성할 수 있었다.

 

취업과 블로그의 휴식기 그리고 글또

 올해 중순에 첫 취업을 하면서 회사와 타지 생활에 적응하다 보니 블로그에 신경을 쓰지 못했고, 1년 가까이 꾸준히 유지해 오던 블로깅 습관이 한 달 만에 사라지게 되었다.

 

 회사 생활도 좀 안정되고 원룸도 구하고 현생이 좀 최적화(?) 되면서 미루고 있던 것들과 해보고 싶었던 것들에 신경 쓸 여유가 생기게 되었다. 그래서 자연스럽게 블로깅을 다시 시작해 보자고 다짐을 했었는데, 평일에는 퇴근하고 또 모니터 앞에 앉아 글을 쓰는 것이 싫어서 주말로 미루게 되었고, 주말에는 주말은 좀 쉬고 싶다는 생각이 들어 다시 미루는 사이클을 무한 반복하다 보니 글쓰기를 시작조차 못하고 있었다.

 

 그래서 혼자서는 못하겠다 싶어서 글쓰기 모임에 대해서 찾아보게 되었고, 우연히 온라인으로 진행하는 개발자 글쓰기 모임인 글또를 발견하고 참여하게 되었다.

 

여전히 글쓰기가 어렵다

 기존해 해왔던 글쓰기이기에 쉬울줄 알았지만 사라졌던 습관을 단기간에 다시 만드는 것은 생각보다 쉽지 않았다. 백수일 때는 미루고 미뤄도 남는게 시간이었기에 글 쓸 시간이 차고 넘쳤지만, 직장인이 된 지금은 한 번 미루고 다른 일에 시간을 쓰면 글을 쓸 시간이 남지 않는다. 더 심각한 문제는 미루고 미뤄 주말에 어찌저찌 글을 쓰려고 하면 어떤 글을 써야할지, 어떻게 써야할지가 막막하다는 것이다.

 

글쓰기 세미나를 듣고서

 그러던 와중에 글또에서 성윤님이 글쓰기 세미나를 강연해주시기로 하셨고, 세미나라도 들어보면 생각이 정리가 되지 않을까 싶어서 참여하게 되었다. 초반부터 사람들이 글쓰는 것에 있어서 어떤 어려움들을 느끼는지에 대해 설명해주시는데, 대부분의 항목들이 현재 내가 느끼고 있는 부분들이라 많이 공감되었다.

 

내가 느끼고 있던 어려움

 세미나를 들으면서 내가 글을 쓰게된 계기, 누구를 위해 글을 쓰는지, 글쓰기를 통해 이루고자 하는 목표 등에 대해 돌아볼 수 있는 시간을 가질 수 있었고, 현재 글 쓰는 것이 어렵다고 느껴진 실질적인 이유를 알 수 있었다.

 

 가장 큰 문제는 내가 작성한 글에 대해 자신감이 없다는 것, 그리고 모순적이지만 자신감이 너무 넘쳤다는 것이다. 현재까지 글또 활동을 하면서 가장 어려웠던 것은 주제를 선정하는 부분이다. 이제 학생도 아니고 현직자인데 단순 정리글을 써도 괜찮을까라는 생각이 들게 되면서 자연스럽게 주제를 선정하는 것이 힘들어졌다.

 

 지금 생각해보면 취업한지 이제 6개월된 사람이 알면 뭐 얼마나 잘 알고 특별하다고 근거 없는 자신감에 저런 생각을 했는지 꿀밤을 한 대 때려주고 싶다.

 

나를 위한 글부터 쓰자

 그래서 일간 조회수 평균 10명도 안나오는 마당에 남들 신경 쓰지 않고 첫번째 독자인 나를 위한 글부터 써보려 한다. 글의 주제도 상관 없고, 내용, 완성도, 기타 자잘한 것들 신경 쓰지 않고 하나든 여러 개든 꾸준하게 글을 쓰는 것부터 다시 시작해보려 한다.

 

마치며

 이번 주에 어떤 글을 써야할지 방황하고 있었는데, 가뭄에 단비처럼 세미나를 열어주셔서 소재를 제공해주신 성윤님께 감사하다는 말씀을 전하며 누가 볼진 모르겠지만 다들 다가오는 한 주도 파이팅하시길 바랍니다! 월요일 싫어

 

'Diary' 카테고리의 다른 글

2024년 회고  (2) 2024.12.15
10일 동안 글자 하나 안 쓴 사람의 다짐글  (1) 2024.10.08
삶의 지도  (0) 2024.09.12
백준 근황  (0) 2024.07.27
솔브닥 마라톤 1코스 완주  (0) 2024.06.09
728x90

개인 프로젝트에 문서화가 필요할까?

 최근에 직접 만든 블로그를 운영해보고 싶어서 퇴근 후에 틈틈이 진행 중인 사이드 프로젝트가 있다. 혼자 진행하다 보니 직접 작성하고, 직접 사용할 텐데 문서화가 필요할까라는 생각이 들어 문서화는 생각하지 않고 개발 중이었다.

 

 하지만 최근에 회사에서 레거시 프로젝트 유지보수를 하면서 문서화의 필요성을 느꼈고, 가장 큰 요인으로는 스스로의 기억력에 자신도 없다. 또한, 앞날은 모르기 때문에 프로젝트의 규모가 예상보다 더 커진다거나, 공개 프로젝트로 변경될 가능성도 생각했을 때 개인 프로젝트라도 문서화를 해둬서 손해 볼 이유는 없다.

 

 다행히도 현재 프로젝트는 인증이나 로깅 같은 초기 공통 로직들을 구현하는 시작 단계에 있어서, 이미 만들어둔 API들을 몰아서 문서화를 해야 되는 끔찍한 일은 없기에 문서화를 도입하기에 문제는 없다.

 

왜 REST Docs인가?

 스프링에서는 문서화를 위해 대표적으로 REST Docs와 Swagger를 사용한다. 하지만, 개인적으로 Swagger는 API 로직이 지저분해지고, API의 신뢰성이 보장되지 않기에 선호하지 않는다. 그래서, 기존에 사용해 봤고, 테스트 코드도 꼼꼼히 작성하면서 진행할 것이기 때문에 REST Docs를 사용하는 것으로 방향을 정했다.

 

사용해 보기

 REST Docs는 API의 테스트를 통해 요청과 응답으로 자동으로 문서 스니펫을 만들고, 해당 스니펫을 작성해 둔 아스키독 파일 양식대로 조합해 문서를 완성하기 때문에, 컨트롤러 단위 테스트에서 문서화를 진행했다.

@Test
@DisplayName("프로필 조회 성공 테스트")
void getMember() throws Exception {
    // given
    ...

    // when
    ResultActions actions = mockMvc.perform(
        get("/members")
            .header(AUTHORIZATION, TOKEN)
            .accept(APPLICATION_JSON)
    );

    // then
    actions.andDo(print())
        .andExpect(status().isOk())
        .andExpect(content().string(apiResponse));

    // restdocs
    actions.andDo(documentHandler.document(
        requestHeaders(
            headerWithName(AUTHORIZATION).description("액세스 토큰")
        ),
        responseFields(
            fieldWithPath("data.memberId").description("회원 아이디"),
            fieldWithPath("data.email").description("회원 이메일"),
            ...
            fieldWithPath("code").description("응답 코드"),
            fieldWithPath("status").description("응답 상태"),
            fieldWithPath("message").description("응답 메시지")
        )
    ));
}

 

 사용하는 방법은 크게 어렵지 않은데, 기존의 테스트 방식에 문서화를 하는 부분만 추가된다. 요청과 응답에 포함되는 헤더와 바디의 정보만 입력해주면 되는데, 오타나 잘못된 경로가 있으면 문서화가 실패하기 때문에 잘 확인하고 적어주기만 하면 된다.

 

생각보다 귀찮은 문서화

 문서화 작업을 하다 보면 요청과 응답의 정보를 일일히 적어주는 귀찮음은 어떻게 해결할 수 없지만, 문서 포맷 설정, 인코딩 설정, 기타 설정, 공통 정보 등 반복적으로 동일한 로직이 사용되는 부분들이 많다.

@MockBean(JpaMetamodelMappingContext.class)
@WebMvcTest({
	...
})
@ExtendWith({RestDocumentationExtension.class})
public class ControllerTest {
    ...
    
    // 공통 전처리
    @BeforeEach
    void setUp(WebApplicationContext context,
        final RestDocumentationContextProvider restDocumentation,
        TestInfo testInfo) {

        // 기타 공통 설정들
        ...

        documentHandler = document(
        preprocessRequest(prettyPrint()),
        preprocessResponse(prettyPrint())
        );

        DefaultMockMvcBuilder mockMvcBuilder = webAppContextSetup(context)
        .apply(documentationConfiguration(restDocumentation))
        .addFilters(new CharacterEncodingFilter("UTF-8", true));

        ...

        mockMvc = mockMvcBuilder.build();
    }
}

 

 매번 문서화 작업마다 반복되는 설정들을 위와 같이 @BeforeEach를 통해 처리하면 조금 더 간편하게 작업이 가능해진다.

actions.andDo(documentHandler.document(
    ...,
    responseFields(
        ...,
        fieldWithPath("code").description("응답 코드"),
        fieldWithPath("status").description("응답 상태"),
        fieldWithPath("message").description("응답 메시지")
    )
));

actions
    .andDo(
        documentHandler.document(
            ...,
            pageResponseFields(
                ...,
                fieldWithPath("pageInfo").description("페이지네이션 정보"),
                fieldWithPath("pageInfo.page").description("현재 페이지"),
                fieldWithPath("pageInfo.size").description("페이지 사이즈"),
                fieldWithPath("pageInfo.totalPage").description("전체 페이지 수"),
                fieldWithPath("pageInfo.totalSize").description("전체 데이터 개수"),
                fieldWithPath("pageInfo.first").description("첫 페이지 여부"),
                fieldWithPath("pageInfo.last").description("마지막 페이지 여부"),
                fieldWithPath("pageInfo.hasNext").description("다음 페이지 존재 여부"),
                fieldWithPath("pageInfo.hasPrevious").description("이전 페이지 존재 여부"),
                fieldWithPath("code").description("응답 코드"),
                fieldWithPath("status").description("응답 상태"),
                fieldWithPath("message").description("응답 메시지")
            )
        )
    );

 

 위의 코드처럼 단일 및 페이지 응답에서 매번 반복적인 로직들을 적어주고 있는데, 유틸 클래스를 만들어 분리해주면 좋다.

private static final FieldDescriptor[] pageInfoFields = new FieldDescriptor[]{
    fieldWithPath("pageInfo").description("페이지네이션 정보"),
    fieldWithPath("pageInfo.page").description("현재 페이지"),
    fieldWithPath("pageInfo.size").description("페이지 사이즈"),
    fieldWithPath("pageInfo.totalPage").description("전체 페이지 수"),
    fieldWithPath("pageInfo.totalSize").description("전체 데이터 개수"),
    fieldWithPath("pageInfo.first").description("첫 페이지 여부"),
    fieldWithPath("pageInfo.last").description("마지막 페이지 여부"),
    fieldWithPath("pageInfo.hasNext").description("다음 페이지 존재 여부"),
    fieldWithPath("pageInfo.hasPrevious").description("이전 페이지 존재 여부")
};

private static final FieldDescriptor[] responseStatusFields = new FieldDescriptor[]{
    fieldWithPath("code").description("응답 코드"),
    fieldWithPath("status").description("응답 상태"),
    fieldWithPath("message").description("응답 메시지")
};

 

 페이지 정보와 응답 상태 정보 같은 고정된 공통 값들을 필드로 만들어두고

public static ResponseFieldsSnippet pageResponseFields(FieldDescriptor... responseFields) {

    List<FieldDescriptor> allFields = new ArrayList<>();
    allFields.addAll(Arrays.asList(responseFields));
    allFields.addAll(Arrays.asList(pageInfoFields));
    allFields.addAll(Arrays.asList(responseStatusFields));

    return responseFields(allFields);
}

public static ResponseFieldsSnippet singleResponseFields(FieldDescriptor... responseFields) {

    List<FieldDescriptor> allFields = new ArrayList<>();
    allFields.addAll(Arrays.asList(responseFields));
    allFields.addAll(Arrays.asList(responseStatusFields));

    return responseFields(allFields);
}

 

 API마다 다른 응답 데이터들만 파라미터로 받아 공통 응답들과 합쳐주면 매번 같은 작업을 하는 번거로움을 줄일 수 있다.

 

마치며

 API의 신뢰성을 챙기면서 테스트 과정에서 편리하게 문서화를 할 수 있는 REST Docs지만, 그럼에도 불구하고 아직도 귀찮은 부분들이 많이 존재한다. 요청과 응답에 대해 직접 명시해주는 것도 자동화할 수 있으면 작업 속도가 더욱 향상될거 같은데, 해당 부분도 좀 더 찾아봐야 할 것 같다.

 

 글을 쓰면서도 개선할 수 있을 것 같은 부분을 발견했고, 작업을 하면서 REST Docs를 찾아보는 과정에서도 다양한 활용 사례를 많이 발견해서, 기회가 되면 다음에 더 나아진 REST Docs 활용글을 작성해보겠다. (이런 말 하면 글이 끊기던데...)

728x90

미루지 말고 첫 주에 블로깅을 끝내자던 사람은 어디 가고 이제야 다짐글을 쓰게 된 스스로를 돌아보며 지킬 수 있는 소소한(?) 다짐글을 작성해보려 한다.

 

 

글또 OT를 들을 당시의 다짐

 한창 글또 OT 이후에 갓생에 대해 열정이 넘칠 때 정해두었던 목표는 다음과 같다.

  • 첫 글은 다짐글이 아닌 개발 관련 포스팅하기
  • 매주 블로깅 하기
  • 노패스로 글또 마무리하기
  • 사이드 프로젝트 진행하기
  • 주에 3회 이상은 퇴근 후에 운동하기
  • 백준 매일 한 문제 이상 풀기

 무식하면 용감하다더니... 성윤 님이 첫 글은 다짐글 쓰는 게 좋을 거라고 말해주셨을 때 들었어야 했다.

 

 

현재

 겸손해진 현재의 목표는 다음과 같다.

  • 바뀐 목표
    • 첫 글은 다짐글
    • 2주에 하나라도 블로깅 하기
    • 패스 가끔은 써도 괜찮을지도...?
  • 지켜지고 있는 목표
    • 사이드 프로젝트 진행하기
    • 주에 3회 이상은 퇴근 후에 운동하기
    • 백준 매일 한 문제 이상 풀기

 취준 시절에는 매일 개인 학습이나 프로젝트를 진행하니 쓸 내용이 많아 어떤 글을 작성해야 할지 주제에 대한 고민이 없었는데, 일을 다니면서 내가 경험한 것들을 글로 작성하려니 어떤 것을 써야 할지부터가 막막해진다. 그래서 우선은 사이드 프로젝트를 진행하면서 고민도 해보고, 이것저것 시도해 보면서 경험한 것들을 포스팅해보려 한다.

 

 사실 사이드 프로젝트와 운동은 이번 주부터 지켜지고 있는 목표라 양심상 뺄까 했지만, 가끔은 뻔뻔할 줄도 알아야 하기에 일단 넣고 유지해 보는 것을 목표로 하려 한다.

 

 

마치며

 습관이란게 당장 만들고 싶다고 만들 수 있는게 아닌데 의욕만 넘쳐서 너무 높게 기준을 잡고 시작했던 것 같다. 꾸준하게 하는게 가장 중요하다 생각해서, 일단은 당장 할 수 있다고 판단되는 기준을 정해서 지켜나가기로 했다.

 

 다짐글을 쓰기 전에 같은 조의 두 분이 올리신 Postgre 글과 specification pattern 글을 보면서 글을 정말 잘 쓰신다는 생각이 들었는데, 좋은 글을 읽는 것만으로도 어떻게 글을 쓰면 좋을지 배워가는 느낌이 들어서 다른 분들이 글을 올리실 때마다 열심히 읽어보는 것도 중요할 것 같다.

 

 글 재주가 있는 편도 아니고, 현재까지 블로그에 작성한 포스트들도 개념을 정리하기만 했다는 느낌이 강해 아쉬운 부분이 많았는데, 이번 기회에 많이 배워서 경험한 것들을 글로 잘 풀어내고, 언젠가 내 글을 읽어줄 다른 사람들에게 내가 경험한 것들이 도움이 될 수 있으면 좋겠다.

'Diary' 카테고리의 다른 글

2024년 회고  (2) 2024.12.15
블로깅 돌아보기  (0) 2024.11.24
삶의 지도  (0) 2024.09.12
백준 근황  (0) 2024.07.27
솔브닥 마라톤 1코스 완주  (0) 2024.06.09

+ Recent posts