미리보기
기본 정보
꾸준히 성장하기 위해 노력하는 개발자입니다. 겸손함을 기반으로 하루하루 성장하기 위해 노력합니다. 같이 일하기 좋다고 느끼는 개발자가 되고 싶습니다.
기술 스택
Java, Spring Boot, Kafka, JPA, MySQL, PostgreSQL, Docker, Kubernetes
경력
주식회사 케이티
사원 | 개발부서 | 재직 중
2022.05. ~ 재직 중 (2년 8개월)
Java와 Spring 을 활용하여 환경 측정기 디바이스 관제 시스템 API 개발을 담당했습니다.
Apache Kafka, Redis, Spring for Apache Kafka 등을 활용하여 측정 장치 데이터 파이프라인 고도화 개발 및 운영을 담당했습니다.
Apache Flink 를 활용하여 IoT 디바이스 측정 데이터에 대한 이벤트 처리 스트림 처리 시스템을 개발했습니다.
측정 장치 데이터 파이프라인 고도화 프로젝트를 진행하며 확장성과 원활한 운영에 대해 지속적으로 고민하고 있습니다.
하나의 어플리케이션을 4개의 어플리케이션으로 분리하여 유연한 기능 추가와 확장성 있는 시스템을 구축했습니다.
원활한 운영을 위해 Prometheus, Grafana 등의 운영 시스템을 구축하였습니다.
다양한 프로젝트를 경험하면서 팀원들간의 원활한 커뮤니케이션을 위해 노력을 기울이고 있습니다.
데이터 파이프라인의 전체 과정이 4000줄 이상의 하나의 메서드로 되어 있던 레거시 시스템의 로직을 분석하여 Wiki에 정리 및 팀원들에게 공유하였습니다.
새로운 개발자가 투입된 경우 시간을 따로 마련해 작성한 코드를 공유하는 시간을 가졌습니다.
프로젝트
환경 측정 장치 데이터 파이프라인 고도화 개발 프로젝트
주식회사 케이티
2023.01. ~ 2023.10.
기존 파이프라인 로직 정리 및 히스토리 파악 및 프로젝트 규칙 설정
Kafka를 이용한 데이터 파이프라인 구조에 대해 Confluence Wiki를 통해 관련 내용을 정리 하도록 팀 내 규정 및 History 작성 제안을 통해 서비스 로직에 대한 이해 및 개발 환경 개선
모노 레포 및 멀티 모듈 프로젝트 구성 시 모듈 이름과 Kafka Topic 이름 설정에 대한 규칙 정리
기존 모노리틱 서비스를 4개의 Java 어플리케이션으로 구성된 형태의 파이프라인으로 구조 개선
기존 Spring Boot 하나의 어플리케이션에서 진행하던 로직을 Kafka와 함께, 데이터 해석 및 그룹화 → 전처리 → 보정,QC → DB에 데이터 저장, 총 4개의 Spring Boot 어플리케이션으로 분리 개발
전처리 로직에서 기존에 모두 if 문으로 분기 처리된 로직을 Interface 와 전략 패턴을 응용하여 유지 보수하기 쉬운 코드로 개선
어플리케이션을 분리하고 이를 Kafka 를 통해 연결하는 구조였기 때문에 파이프라인과 관련 없는 기능이라면 새로운 어플리케이션을 개발하고 원하는 토픽에 연결함으로써 기능 확장에 유리하도록 설계
기존에 측정 데이터를 단건마다 DB에 Insert 하는 로직을 Bulk Insert 로 변경하여 DB 저장 시간이 더 빨라지도록 구현
Kafka 클러스터 3대, 각 어플리케이션은 2개씩 배포하여 고가용성 확보
안정적인 운영을 위해 Grafana, Prometheus 등의 스택을 이용하여 Kafka 관련 대시보드 구축
어플리케이션 로그를 편리하게 보기 위해 로그 Grafana-Promtail-Loki 시스템 구축
IoT 이벤트 엔진 설계 및 개발
주식회사 케이티
2024.01. ~ 2024.05.
여러 IoT 장비 간 CEP(Complex Event Processing) 처리를 위한 Apache Flink 도입
Flink Stream API를 활용하여 데이터 처리 후 적재, 이벤트 판단 등 파이프라인 설계 및 개발
Flink Keyed State를 이용해 디바이스 측정 데이터가 정상적으로 올라오지 않는 장애 상황에 대한 이벤트 처리 기능 개발
Apache Flink를 활용하여 스트리밍 단순 이벤트 처리 로직 구현
여러 단계의 대소/동등 비교 등의 수식 처리를 Java Object로 파싱하여 판단하는 로직 구현
Java 인터페이스를 활용한 다형성과 재귀 로직을 이용하여 이벤트 판단 로직 구현
핵심 로직인 수식 판단 로직의 도메인 클래스의 경우 TDD를 활용하여 엣지 케이스에 대한 버그를 최소화하며 개발 진행
이벤트 관리에 필요한 API를 제공하는 Spring 어플리케이션 개발
Spring Data JPA 를 기본으로 사용하고, 동적 쿼리는 QueryDSL을 이용하여 개발
이벤트에 관한 CRUD 작업이 발생하면 Redis에 해당 내용을 업데이트 하고 Flink Job 에서 사용하도록 설계
포트폴리오
교육
경희대학교
대학교(학사) | 기계공학과
2016.02. ~ 2022.02. | 졸업
대외활동
ATDD, 클린 코드 with Spring 9기
NEXTSTEP
TDD 교육에서 끝나지 않고 실제 Spring Boot 와 Spring Data JPA 를 활용하여 요구사항을 받고 인수테스트를 작성한 후 이에 맞게 도메인 객체에 대한 TDD 를 진행하는 방법에 대해 학습했습니다.
Cucumber 를 이용한 거킨언어를 사용해 테스트를 작성하고 문서화하는 방법에 대해 배울 수 있었습니다.
Spring Data JPA를 좀 더 객체지향적인 ORM에 맞게 사용하고 요구사항을 구현하였습니다.
TDD, 클린 코드 with Java 17기
NEXTSTEP
Java 기반의 여러 미션을 진행하며 TDD 방법론에 대해 학습할 수 있었습니다.
객체지향적인 코드를 위해 어떻게 객체를 설계하며 어떤 코드가 테스트 하기 쉬운 코드인지 배울 수 있었습니다.
자기소개
[프로젝트 경험]
1. Java 와 Spring Framework 기반의 서비스 개발 경험
데이터 파이프라인 고도화 프로젝트를 진행했습니다. 해당 프로젝트는 Java, Spring Boot, Spring for Apache Kafka를 사용하여 진행했습니다. 파이프라인 로직 중 Kafka에 전달된 데이터를 애플리케이션에서 사용하는 DTO 형식에 맞도록 전처리하는 기능이 존재했습니다. 이때 문제는 Kafka에 크게 실내 측정기, 실외 측정기, 환기장치, 산소발생기 4개의 디바이스의 데이터가 들어오는데, 해당 디바이스 종류별로 전처리 과정이 조금씩 다른 문제가 있었습니다.
이때 Java의 인터페이스를 활용하여 다형성을 이용해 유연하게 코드를 작성하도록 구현했습니다. 기존 로직의 경우, if 문을 사용하여 4가지 디바이스 경우에 대해 나눠서 처리를 하였지만 데이터 태그들이 한두 개가 아닌 몇십 개의 태그들이 존재했기 때문에 코드의 가독성과 유지보수성이 좋지 않다고 느꼈습니다. 따라서 데이터를 전처리하는 인터페이스를 하나 정의하고, 각각의 디바이스에 대한 전처리 구현체 4개를 따로 만들어서 사용했습니다. 이때 스프링의 빈 주입을 통해 4개의 구현체를 Map 형식으로 주입받고 Key 값을 디바이스 종류로 설정하였습니다. 이후 Kafka로 들어오는 Key 값에 어떤 종류의 디바이스 장치인지 정보가 있었기 때문에 해당 정보를 이용하여 들어오는 데이터마다 Map에서 구현체를 선택해 전처리를 진행하도록 구현했습니다.
이렇게 구현하여 이후 특정 디바이스의 전처리 로직에 수정이 필요한 경우 해당하는 구현체만 수정하면 되기 때문에 유지보수에 유용한 구조가 되었고, 만약 새로운 디바이스가 추가된다면 해당 디바이스에 맞는 구현체를 구현하고 빈으로 추가 등록만 하면 되기 때문에 객체지향 원칙 중 개방-폐쇄 원칙을 지키는 구조를 가져갈 수 있었습니다.
2. ElasticSearch 8.11.1 버전 인덱스 설계 경험
처음 인덱스를 설계할 때 문자열 프로퍼티에서 어떤 필드를 “text” 타입으로 두고 어떤 필드를 “keyword” 타입으로 설계하는지에 대해 고민이 있었습니다. “text” 타입은 전문 검색에 적합하고 “keyword” 타입은 단순 완전 일치 검색에 적합하기 때문에, 어떤 필드로 검색할지를 먼저 고려했습니다. 서비스 요구사항으로는 제목과 본문으로 검색을 하기 위해 제목과 본문 필드는 “text” 타입으로 설정하고, 다른 대부분의 필드는 “keyword”로 설정했습니다. 이때 이후에 저널리스트 이름으로도 검색할 수 있을 것이라는 서비스 확장을 염두에 두고 추가될 만한 필드는 “text” 타입으로 설정했습니다.
다음으로 “text” 타입으로 설정한 필드의 애널라이저는 저장된 데이터 형식에 맞게 캐릭터 필터, 토크나이저, 토큰 필터를 조합한 커스텀 애널라이저를 사용했습니다. ElasticSearch에 저장된 기사 내용과 제목이 HTML 형식으로 저장되어 있었기 때문에 HTML 태그를 삭제해주는 html_strip 캐릭터 필터를 적용하였고, 토크나이저는 한글 분석을 위해 nori_tokenizer를 사용했습니다. 마지막으로 토큰 필터는 nori_part_of_speech와 nori_readingform 필터를 적용하여 검색 성능을 높이기 위해 사용했습니다.
다음으로 주기적으로 데이터를 삭제하기 위해 ILM(Index Lifecycle Management) 기능을 적용하여 60일이 지난 인덱스는 삭제하도록 설계하였습니다. 이를 위해 동일한 인덱스 설정을 갖도록 인덱스 템플릿을 정의하고 날짜에 따라 인덱스를 생성하도록 했습니다. 이후 ElasticSearch의 Alias 기능을 이용해 날짜별 인덱스를 하나의 인덱스 이름 별칭으로 조회하도록 인덱스를 설계하였습니다.
3. 새로운 기술 스택을 학습한 경험
IoT 이벤트 엔진 설계 및 개발 프로젝트를 진행할 때 Apache Flink를 도입하여 사용했습니다. Flink를 도입한 이유로는, 우선 IoT 디바이스에서 Kafka Topic에 측정 데이터가 올라오는데, 해당 데이터를 실시간으로 스트림 처리하여 데이터베이스에 저장해야 하는 스트리밍 처리가 필요했습니다.
다음으로 강력한 상태 저장 처리가 필요했습니다. 특히 특정 디바이스에서 주기적으로 데이터가 올라오지 않는 경우, 해당 상황을 인지하고 디바이스에 문제가 있다고 판단하기 위해 디바이스별로 Keyed State를 이용한 상태 처리가 필요했으며, Flink가 이러한 상태 처리에 강점이 있다고 판단했습니다.
마지막으로 CEP(Complex Event Processing) 기능을 이용한 이벤트 처리 요구사항이 있었는데, Flink에서는 API로 이를 제공했기 때문에 요구사항에 필요한 기능이 적절하게 갖춰져 있다고 판단하여 해당 기술을 선택했습니다.
처음 Flink라는 기술을 학습하기 위해 가장 먼저 공식 문서를 이용했습니다. 강의나 책을 보는 것도 좋지만 상대적으로 자료가 부족했기 때문에 공식 문서가 가장 좋은 자료라고 판단했습니다. 기본적인 Flink의 개념을 이해하기 위해 공식 문서를 읽고 개인적으로 간단한 데모 프로젝트를 구현하여 Flink에서 제공하는 여러 API를 테스트한 후, 원하는 요구사항을 구현할 수 있겠다고 판단하여 기술을 선택했습니다.
다음으로 Kafka와 ElasticSearch를 처음 학습할 때는 공식 문서뿐만 아니라 여러 좋은 책이나 강의가 많았기 때문에 외부 자료들을 많이 이용했습니다. Kafka의 경우 “실전 카프카: 개발부터 운영까지”라는 책으로 학습을 했고, ElasticSearch는 “엘라스틱서치 바이블”이라는 책으로 학습했습니다.
학습 이후에는 반드시 개인적으로 간단한 프로젝트라도 만들어서 학습한 내용을 직접 코드로 작성하는 시간을 갖기 위해 노력합니다. Kafka의 경우 Spring 환경에서 Spring for Apache Kafka를 이용해 사용했는데, 관련된 설정을 이해하기 위해 Spring 공식 문서를 확인하며 테스트를 진행했습니다. 컨슈머의 싱글 모드 혹은 배치 모드 설정이나 자동 커밋, 프로듀서의 중복 없는 전송, 정확히 한 번 전송 등의 설정을 어떻게 하는지 직접 테스트하며 확인했습니다.
ElasticSearch의 경우 직접 프로젝트를 진행하며 책에서 읽었던 Alias, Reindex, 커스텀 애널라이저 기능 등을 직접 적용하면서 작동 방식을 이해할 수 있었습니다.
이처럼 새로운 기술 스택을 학습해야 하는 경우, 가장 먼저 공식 문서나 관련된 강의 혹은 책을 통해 대략적으로 어떤 기능을 하는지 확인하는 시간을 갖습니다. 그 후 반드시 직접 코드로 구현하며 테스트해보고 학습한 기능들을 제 것으로 만들기 위해 노력합니다.
4. 비효율적인 시스템 구조를 개선한 경험
데이터 파이프라인 고도화 프로젝트에서는 기존 하나의 애플리케이션으로 이루어진 Spring 애플리케이션을 크게 4개의 Spring 애플리케이션과 Kafka를 이용해 분리하는 과정을 거쳤습니다. 각 애플리케이션의 역할은 다음과 같습니다. 먼저, Avro 형식의 데이터를 코드에서 사용할 DTO 형식으로 변환하는 역할, 다음으로 해당 DTO의 값을 전처리하여 DB에 저장할 칼럼 형식으로 변환하는 역할, 데이터 보정을 진행하는 역할, 마지막으로 DB에 데이터를 저장하는 역할이 있습니다.
가장 먼저 기존 파이프라인의 로직을 파악하는 것이 중요했습니다. 메서드 하나로 이루어진 로직이었는데, 해당 로직에 대해 정확하게 알고 있는 사람이 없었기 때문에 전달받은 코드를 하나하나 분석했습니다. 이러한 과정에서 시퀀스 다이어그램을 작성하고 로직을 파악하기 위한 작업들을 진행했으며, 요구사항을 정리했습니다. 예를 들어, 기존 코드에는 실외, 실내 공기에 대한 보정 과정이 있었는데, 해당 과정은 사실 실외 공기에 대한 보정만 진행하면 되는 요구사항이었습니다. 이를 팀장님과 공유하고 다른 부서 사람들과의 회의를 통해 요구사항을 좀 더 명확하게 확립할 수 있었습니다.
다음으로, 좀 더 유연하고 확장성 있는 구조에 대해 고민했습니다. 데이터는 크게 실내 측정기, 실외 측정기, 환기 장치, 산소 발생기 4개의 종류가 있었습니다. 단일 애플리케이션을 4개의 애플리케이션으로 나눈 이유는 중간에 Kafka를 이용해 데이터를 넘겨주는데, 해당 토픽에 추가적인 기능을 갖는 애플리케이션이 추가될 수 있기 때문에 확장성을 염두에 두고 설계했습니다. 실제로 중간 토픽에서 외부에 전달하는 데이터를 따로 필터링해서 전달하는 애플리케이션도 나중에 유연하게 확장할 수 있었습니다.
결국, 기존 하나의 메서드로 이루어져 있던 파이프라인 과정을 애플리케이션 4개와 Kafka를 이용한 형식의 파이프라인으로 고도화했습니다. 저는 해당 모듈에서 DTO를 DB에 저장할 칼럼 형식으로 변환하는 모듈과 보정하는 모듈의 개발을 주로 맡아서 진행했습니다. 이후 정상적인 운영을 위한 모니터링 관련 정책과 배포 작업 정책을 수립하여 몇 달간의 테스트 이후에 큰 문제 없이 배포할 수 있었습니다.