포트폴리오(20241026)

영역 · 자원
고정하기
3 more properties

정보 공유에 진심인 개발자

발표를 통한 정보 공유에 능한 개발자

서비스가 가치를 제공하도록 만들기 위해선 개발자로서 문제를 어떻게 해결할지 고민해야 합니다. 또한, 이러한 고민을 동료와 나아가 다른 개발자들과 공유하는 것은 매우 중요한 역량이라 생각합니다. 그리고, 저는 이런 역량을 갖춘 개발자입니다.
라이트닝 토크_내 테스트코드가 짱임_로빈.pptx
17570.9KB
라이트닝 토크 - 위키 털린 썰 푼다.pptx
2580.3KB
라이트닝 토크_로빈.pptx
18928.7KB
우아한테크코스에서의 활동명인 로빈으로서 위와 같이 많은 발표를 통해 크루들과 경험을 공유하고 함께 성장하는 문화를 이끌어갔습니다.

글을 통한 정보 공유에 능한 개발자

같은 맥락에서, 개발자로서 글을 통해 정보를 공유하는 것은 중요한 역량 중 하나라고 생각합니다. 저는 그 중요성을 인식하고, 개발 과정에서의 경험과 학습한 내용을 블로그에 오랫동안 기록해왔습니다.
작성한 블로그 게시글 목록 일부

불편함을 개선할 의지가 있는 개발자

OOP와 디자인 패턴을 활용한 데이터베이스 접근 기술의 점진적 변경

자세한 내용은 블로그 글에서 확인할 수 있습니다!
우아한테크코스 미션 수행 과정에서 데이터베이스 접근 기술을 JdbcTemplate에서 Spring Data JPA로 변경하는 요구사항이 있었습니다. 그런데 이 과정에서 데이터베이스 접근 기술만 변경하는데도 논리적으로 문제가 없는 애플리케이션 영역의 테스트가 모두 컴파일 에러가 나는 상황이 발생했습니다.
기존의 인터페이스를 그대로 유지하면서 JPA 를 사용할 수 있는 설계
이를 해결하기 위해 위와 같이 새로운 인터페이스를 추출하여 테스트 코드는 거의 변경하지 않고 데이터베이스 접근 기술을 변경할 수 있었습니다.

Swagger 어노테이션 중복 제거

자세한 내용은 깃허브 PR에서 확인할 수 있습니다!
API의 문서를 만들기 위해 고민한 기술은 springdoc-openapi와 RestDocs였습니다. 이 중 Swagger 기반의 springdoc-openapi를 선택했습니다. RestDocs를 사용하기 위해선 테스트 코드를 RestDocs가 인식할 수 있는 형태로 작성해야 했기 때문이었습니다. 이미 작성한 테스트 코드 구조를 변경할 만큼 RestDocs가 편리하다는 생각이 들지 않았기 때문입니다.
하지만, Swagger의 특성상 문서를 컨트롤러에 어노테이션 형태로 직접 작성해야 했습니다. 특히 오류 응답을 표현하기 위해서는 @ApiResponse, @Content, @Schema 등의 많은 어노테이션을 이용해야 했습니다. 커스텀 어노테이션을 추가해 작성해야 할 어노테이션의 수를 3배 이상 줄였습니다.
커스텀 어노테이션 적용 후

프로젝트

우아한테크코스 6기 개발자 취준생 커뮤니티 - 데벨업

서비스 개요

개발자 취준생은 자신의 미래에 대한 불안을 가지고 있습니다. 그리고 이런 불안은 자신의 실력을 가늠할 수 없어서 생긴다고 판단했습니다. 또한, 다른 개발자들과의 소통을 원하는 취준생이 많다고 판단했습니다. 이런 점에서 우아한테크코스에서 미션을 풀고, 미션과 코드에 대해 다른 크루들과 여러 견해를 나누는 경험을 다른 개발자 취준생들도 해볼 수 있으면 좋겠다는 생각에 서비스를 기획 및 제작하게 되었습니다.

진행 업무

기획
데이터베이스 스키마 설계
필요한 API 설계 및 구현
개발용 인프라 및 운영용 인프라 구축
CI/CD 인프라 구축
TPS 측정 및 데이터 분석 후 개선

풀이 목록 조회 성능 개선

제공한 미션에 대한 풀이 목록은 제출 일자를 기준으로 정렬하여 제공해야 했습니다. 이 과정에서 필요한 모든 컬럼을 한번에 조회하는 쿼리를 사용했습니다. file sort가 발생해 모든 컬럼을 메모리에 올린 상태로 정렬이 수행되고 있었습니다.
이를 개선하기 위해 풀이 식별자와 제출 일자만 먼저 조회하도록 하여 정렬 시간을 단축했고, 식별자를 통해 따로 데이터를 조회하게 하여 10만개 풀이 데이터가 존재하는 상황에서 약 700ms 에서 300ms 정도로 줄었습니다.
모든 컬럼을 메모리에 올린 채 정렬
풀이 식별자와 제출 일자만 메모리에 올린채 정렬
이후 제출 일자에 인덱스를 추가하여 file sort가 발생하지 않게 하여 최종적으로 약 200ms 정도로 응답 속도를 줄였습니다

인프라(AWS)의 점진적 개선

팀 프로젝트로 웹 서비스를 구축하면서 사용자 편의를 보장하기 위해 간단한 인프라부터 시작하여 점점 발전시켰습니다.
초기 개발 시점 백엔드 인프라 구조
모니터링 시스템 구축에 대한 상세한 사항은 깃허브 위키에서 확인하실 수 있습니다!
처음 서비스를 구축한 시점에는 단순하게 하나의 우분투가 설치된 EC2 인스턴스에 직접 Nginx와 Java를 설치하고 Spring Boot 어플리케이션을 구동하는 방식으로 개발을 진행했습니다. 이후 동료 개발자를 대상으로 한 베타 테스트 과정에서 발생한 오류를 기록하고 추적하는 시스템이 없어 빠른 대응이 힘든 문제를 발견했습니다.
모니터링 시스템과 연동된 인프라 구조
분산 인프라 구축에 대한 상세한 내용은 블로그 글에서 확인하실 수 있습니다!
이를 해결하기 위해 모니터링 및 로깅 시스템을 도커와 도커 컴포스를 사용해 구축했습니다. 모니터링 및 로깅 시스템은 Nginx와 Java와 달리 설치 후 설정해줘야하는 것이 많았습니다. 따라서, 이를 우분투에 직접 설치하고 설정하는 것 보단 도커와 도커 컴포스를 사용해 코드의 형태로 의존성을 관리하는 것이 유리하다고 판단했습니다.
SPOF를 제거해 가용성을 높인 인프라 구조
이후 개발이 진척되어 실제 사용자를 대상으로 서비스 런칭을 앞두고 배포 과정 도중이나 WAS나 DB등에 문제가 발생하는 경우에도 서비스 사용자가 서비스를 그대로 사용할 수 있도록 로드밸런서와 WAS, DB 다중화를 도입했습니다.

CI/CD 점진적 개선

자세한 구축 과정은 블로그 글에서 확인하실 수 있습니다!
여러 팀원이 개발한 기능을 빠르게 통합해 테스트하고자 했습니다. 이를 구현하는 수단으로 GitHub Actions, Jenkins, AWS 코드 파이프라인이 거론되었습니다. 이 중 GitHub Actions를 선택한 이유는 다음과 같습니다.
1.
코드를 깃허브로 관리하기 때문에 CI/CD도 깃허브로 관리하면 관리 포인트가 줄어든다.
2.
여러 오픈소스를 활용해 파이프라인을 구축할 수 있다.
이를 통해 구축한 CI/CD 파이프라인은 다음과 같습니다.
무중단 CD 구축 과정에서 논의한 내용은 블로그 글에서 확인하실 수 있습니다!
서비스의 가용성을 높이기 위해 분산 인프라를 구축하고 무중단 CD를 구축해야했습니다. 여러 무중단 배포 방식 중 Rolling 방식을 사용하였고 그 이유는 다음과 같습니다.
1.
A/B 테스트를 계획하고있지 않기 때문에 Canary 방식을 도입할 경우 이점이 없다.
2.
Blue-Green 방식을 이용할 경우 물리적인 서버 자원(EC2 인스턴스)가 2배 더 필요하지만 예산이 없다.
3.
어떤 배포 방식을 사용하던 하위 호환성을 유지해야 하고, 그렇다면 굳이 트래픽의 일괄 전환도 필요 없다.
무중단 배포 과정에서 발생할 수 있는 이상현상을 고려해 다음과 같은 CD 파이프라인을 설계했습니다.
CD 파이프라인 플로우 다이어그램

(주)오큐브 재직 시 진행한 프로젝트

서비스 개요

신입 사원으로 입사한 직후 신규 런칭 예정인 프로젝트에 백엔드 서버 개발자로서 참여했습니다.

진행 업무

클라이언트 어플리케이션 분석을 통한 API 설계 및 구현
인증 시스템 구축

인증 시스템 구축 - JWT와 Redis를 이용한 세션 구현

자세한 내용은 블로그 글에서 확인할 수 있습니다!
REST API와 WebSocket API를 제공하는 두 Web Application Server(WAS)로 구성된 시스템을 개발하고 있었습니다. 각 API는 인증과 인가 작업이 필요했습니다. 구체적인 요구사항은 다음과 같습니다.
1.
인증 정보의 생성은 REST API 서버에서 담당한다.
2.
같은 인증 정보로 분리된 REST API 서버와 WebSocket 서버에서 사용자를 인증할 수 있어야 한다.
3.
사용자는 시스템이 지원하는 클라이언트 플랫폼마다 하나의 기기에서만 로그인할 수 있다.
a.
윈도우, 안드로이드, iOS, Mac을 지원한다.
b.
같은 플랫폼의 다른 기기에서 중복으로 로그인을 시도할 경우 사용자의 선택에 따라 기존에 로그인된 기기를 로그아웃하고, 로그인 시도하는 기기에서 로그인을 할 수 있다. 이런 로그인을 강제 로그인이라 한다.
이를 해결하기 위해 다음과 같은 구조를 설계하고 구현했습니다.
강제 로그인을 하는 상황의 시스템 동작 다이어그램

편의점 할인 상품 모아보기 서비스 - 편킹

서비스 개요

동일한 상품을 편의점마다 다른 할인 행사를 적용하기 때문에, 소비자 입장에서 차익이 발생할 수 있습니다. 따라서, 소비자가 이익을 보기 위해선 각 편의점의 공식 시스템을 통해 할인 정보를 비교해야 합니다. 이런 불편함을 해결하기 위해 하나의 시스템에서 이를 통합해 비교할 수 있게 하고자 했습니다. 나아가 사용자의 사용 패턴을 기반으로 사용자의 취향에 맞는 상품을 추천하는 기능을 추가해 사용자의 편의성을 높이고자 했습니다.
추가적으로, 기존 서비스와의 차별성을 위해 이미지 검색 기능을 제공하여, 텍스트 검색을 하기 힘든 상황에도 서비스를 사용할 수 있도록 했습니다.

진행 업무

프로젝트 리드
사전 설문조사 & 사용자 피드백 수집
데이터베이스 스키마 설계
필요한 API 설계 및 구현
AI 모델과 REST API 서버 연동
데이터 크롤링 시스템 성능 개선

멀티 스레드를 활용한 크롤링 성능 개선

이미지 인식과 사용자별 추천 시스템을 활용한 편의점 할인 상품 모아보기 서비스를 제작하고 있었습니다. 이를 위해 편의점별 할인 상품 정보를 수집해야 했습니다. 각 편의점 공식 홈페이지에서 동적 크롤링을 통해 데이터를 수집할 필요가 있었습니다. 또한 행사 상품은 자정에만 변경되므로 매일 자정에 한 번만 크롤링을 수행해야 했습니다.
처음에는 파이썬을 이용해 동적 크롤링을 수행했으나 약 30분이라는 처참한 실행 속도를 보였습니다. 이를 개선하기 위해 동적 크롤링 대신 정적 크롤링을 수행하여 실행 속도를 약 6분으로 낮출 수 있었습니다.
이후 각 작업이 병렬적으로 수행될 수 있다는 점을 깨닫고 Stream API의 parallelStream을 이용해 병렬로 작업을 수행하도록 하여 실행 속도를 약 3분으로 낮출 수 있었습니다.
병렬 스트림을 사용하여 개선된 코드

AI 모델 로드 시점 변경을 통한 응답 성능 개선

이미지 검색 기능을 제공하기 위해선, 이미지를 분석해 그 이미지가 어떤 상품인지 알아내야 합니다. 상품의 로고등은 글자를 다소 외곡하여 표현하기 때문에 단순 텍스트 인식은 사용할 수 없었습니다. 따라서, 상품의 여러 각도에서의 이미지를 수집해 AI 모델에 학습시켜 검색하는 방법을 사용했습니다.
그런데, AI 모델과의 연동 작업 이후 테스트 수행 과정에서 응답이 8초 정도 걸리는 문제가 발생했습니다. 이는 사용자가 불편함을 느낄 수준이라 판단, 개선 작업에 들어갔습니다. AI 모델의 성능은 그것이 동작하는 컴퓨터의 성능에 큰 영향을 받습니다. 그러나, 당시 예산의 한계로 인해 서버의 물리적 성능을 올릴 수는 없었습니다. 병목 지점을 확인하기 위해 REST API, 상품 정보 데이터베이스, AI 모델을 각각 따로 성능 테스트를 수행했습니다. 그런데, 각 모듈의 응답 속도의 합은 4초정도 되었습니다. 즉, 이들을 연동하는 과정에서 비효율이 발생한다는 것이었습니다.
소스코드를 확인해보니, AI 모델의 결과를 REST API 서버로 전달하기 위해 Flask를 사용할 때 모델의 로드를 엔드포인트 정의부분에서 하고 있는 것을 알았습니다. 즉, HTTP 요청마다 모델을 새로 로드해서 느려지는 것을 알게 되었고 이를 수정해 응답 속도를 4초 내외로 줄일 수 있었습니다.

동아리 홈페이지 개발 및 운영

서비스 개요

정보보안 학술 동아리에서 활동했습니다. 매 주 정해진 시간에 관련 주제의 세미나를 하는 방식으로 활동을 진행했습니다. 세미나에 대한 질문이나, 세미나 자료 공유를 카카오톡을 통해 진행하니 이를 모아보기 힘든 문제가 발생했습니다. 이를 해결하고자 동아리 홈페이지를 만들어 세미나 자료를 공유하고, 이에 대한 질문과 대답을 할 수 있도록 했습니다. 이후 동아리 부원들의 피드백을 통해 간이 해킹 대회 기능 등을 추가하며 홈페이지를 운영했습니다.

진행 업무

사전 설문조사 & 사용자 피드백 수집
데이터베이스 스키마 설계
풀스택 개발

동아리 홈페이지 개발 및 운영 - 소프트웨어 생명 주기 경험

정보보안 학술 동아리 HUST는 주기적으로 보안 관련 세미나를 열어 자신이 공부한 내용을 공유했습니다. 그러나 동아리원이 하나둘 졸업하거나 활동을 그만두면 그 지식이 소실되며, 매년 비슷한 내용만을 반복하는 문제가 있었습니다. 이는 동아리 활동이 개인에게만 쌓이는 것 때문이었습니다. 따라서 각자 학습한 지식을 기록으로 남기면 문제를 해결할 수 있을 것이라 생각했습니다. 그래서 동아리 홈페이지를 만들기로 결정했습니다.
당시 Java를 학교 수업을 통해 막 학습한 시점이었고, 동아리실 구석에 있던 Servlet & JSP 책을 참고하며 열정 하나로 홈페이지를 만들어갔습니다. 단순한 게시판 기능을 시작으로, 동아리 내부 해킹 대회를 쉽게 구축할 수 있도록 문제 출제 기능과 랭킹 기능을 도입했습니다.
하지만, 코로나 19로 인해 동아리 운영이 크게 힘들어졌고, 많은 기능을 도입하면서 코드의 유지보수를 전혀 고려하지 않았기 때문에 더 이상 운영이 어렵다고 판단해 홈페이지를 닫게 되었습니다. 이후 클린 코드와 Spring 프레임워크를 학습하게 되었습니다.

과외 선생님의 학생 관리 서비스 Great

서비스 개요

프로젝트 그레잇은 소규모 학원, 혹은 과외 선생님들이 열정을 부어 지도하는 학생들의 성적관리를 한층 더 편하게 만들어주기 위해 진행한 프로젝트입니다.

진행 업무

기획
데이터베이스 설계
REST API 설계 및 구현
인프라 구축

서로 다른 사이트에서의 쿠키 SameSite 쿠키

자세한 내용은 블로그 글에서 확인하실 수 있습니다.
얻은 지식 : 서로 다른 사이트에서 발급한 쿠키는 SameSite=Lax 옵션을 사용해야 제대로 사용할 수 있다.
과외 선생님의 학생 관리를 위한 서비스 Great 프로젝트에서 인증을 위해 엑세스 토큰과 리프레쉬 토큰을 도입했습니다. 엑세스 토큰은 로그인 응답의 body로 전달했습니다. 리프레쉬 토큰은 로그인 응답에 HttpOnly 쿠키를 이용해 전달했습니다.
그런데, 엑세스 토큰 만료되어 재발급 요청을 보낸 경우 서버에서 쿠키를 받지 못하는 현상이 발생했습니다. 동아리 홈페이지를 개발했을 때는 쿠키는 별도로 설정하지 않아도 항상 전달되었는데 이상하다고 생각했습니다. 구글링을 통해 다른 도메인에서 발급한 쿠키를 사용하기 위해선 SameSite 쿠키 옵션을 사용해야 한다는 것을 알았고 문제를 해결했습니다.
SameSite None 쿠키 사용
그리고 이 경험을 기록한 덕분에 현재 진행중인 데벨업 프로젝트에서 비슷한 문제가 발생했을 때 금방 해결할 수 있었습니다.

목표 달성 독려 시스템 골키퍼

서비스 개요

다이어트를 하고자하는 목표를 세웠으나 작심삼일이 되기 쉬웠습니다. 세운 목표를 타인에게 공개하고 이를 지속적으로 인증하도록 한다면, 목표를 쉽게 포기하지 못할 것이라 생각했습니다. 또한, 이를 같이 하는 사람들이 있다면, 그 효과는 더욱 좋아질 것이라 생각했습니다. 따라서, 이러한 서비스를 만들어보았습니다.

진행 업무

기획
데이터베이스 설계
REST API 설계 및 구현
인프라 구축

SOP와 CORS 그리고 Spring Interceptor

자세한 내용은 블로그 글에서 확인하실 수 있습니다.
얻은 지식: 시스템 전역에 영향을 주는 요소는 주의해서 사용해야 한다. 문제가 발생했을 때 이것이 원인이 될 수 있음을 간과하지 말아야 한다.
골키퍼 프로젝트 진행 당시 처음으로 프론트엔드와 백엔드를 나누어 개발하고 다른 서버에 배포하는 경험을 했습니다. 이때, SOP를 처음 알게 되었고, 서로 다른 사이트에서 스크립트로 API를 호출하기 위해서는 백엔드 코드에 CORS 설정을 해야 한다는 것을 알게 되었습니다.
CORS 설정
하지만, 설정 후에도 여전히 CORS 관련 오류가 발생했습니다. 이유를 알아내기 위해 CORS의 절차를 학습하고, 학습한 절차 중 어떤 것이 문제인지 테스트를 수행했습니다. 그 과정에서 OPTIONS 메서드를 이용한 preflight 요청의 응답이 제대로 전달되지 않는 현상을 확인했습니다.
Spring의 설정은 공식 문서를 보고 진행한 것이기 때문에 잘못되지 않았다고 생각했습니다. 따라서, 요청 전역에 영향을 주는 무언가가 preflight 요청 처리를 방해한다고 판단했습니다. 이후, 인증 작업을 구현한 Interceptor에서 preflight 요청을 거부하고 있는 것을 확인해 오류를 고칠 수 있었습니다.