스크롤하여 경력 보기
Swit
프론트엔드 개발자
다양한 플랫폼(웹/앱)을 지원하는 채팅/칸반 기반의 글로벌 협업 서비스 개발
주요 프로젝트
- 프로젝트 기능 개발 및 유지보수
기여도: 100%
- 리팩토링을 통한 코드 품질 개선
기여도: 90%
- LLM을 통한 채팅 서비스 개발
기여도: 30%
사용한 기술
Swit 프로젝트 관리 기능
모든 프로젝트의 업무 뷰어
Swit 업무 상태 관리
업무 상태 변경 및 관리 기능
Swit 칸반 보드
상태/버킷/담당자 기준의 칸반보드
Swit 대시보드 뷰
프로젝트 진행 상황을 한눈에 볼 수 있는 대시보드

Swit 캘린더 뷰
일정 관리를 위한 캘린더 인터페이스

Swit 리스트 뷰
업무 목록을 상태별로 관리하는 리스트 뷰

Swit 타임라인 뷰
프로젝트 일정을 시간순으로 확인할 수 있는 타임라인

Wins
웹 개발자
AI 기반 네트워크 관제 시스템 웹 파트 개발
주요 프로젝트
- AI 기반 관제시스템 웹서버 개발
기여도: 100%
- AI 기반 관제시스템 UI 개발
기여도: 80%
사용한 기술
ESE
플랫폼 개발자
시설물 통합 관제 시스템 및 영상 통합 관제 시스템 개발
주요 프로젝트
- 영상 통합 관제 시스템 개발
기여도: 40%
- 시설물 통합 관제 시스템 개발
기여도: 30%
- 재난 안전 플랫폼 개발
기여도: 30%
사용한 기술
핵심 가치
개발자로서 추구하는 가치와 방향성
문제에 빠르게 도전
복잡한 문제를 마주했을 때 신속하게 분석하고 효율적인 해결책을 찾아냅니다.
지속적인 성장을 추구
자기 개발을 통해 더 나은 개발자로 성장하기 위해 노력합니다.
아이디어
아이디어를 통해 문제를 해결하는 오픈소스를 개발하여 기여합니다.
문제 해결
UX부터 DX까지 다양한 문제를 마주하고 빠르게 도전
스크롤하여 더 알아보기
Swit 업무 필터링 기능의 UX 통일
문제 상황
- 각 화면(대시보드, 캘린더, 간트차트 등)에서 업무 리스트 필터링 UX가 상이함
- 상태, 담당자, 참여자, 우선순위 등 동일한 필터가 화면마다 다르게 구현됨
- 필터 구현 코드가 화면마다 중복되어 유지보수가 어려움
해결 접근법
- 모든 필터링 옵션을 표준화 할것을 기획적으로 제안
- 모든 화면에서 동일한 필터링 UX를 제공하여 사용자 경험 일관성 확보
- 필터링 로직을 모듈화하여 코드 중복 제거 (4,000라인 → 700라인, 약 82.5% 감소)

통합된 필터링 UI - 모든 화면에서 동일한 사용자 경험 제공
Swit 모노레포 아키텍처 변경
문제 상황
- 짧은 시간에 동시다발적으로 많은 기능을 배포
- 그에 따라 배포시 순환참조로 인해 빌드에러 발생
해결 접근법
- 각 패키지가 단방향으로 참조하도록 설계
- 개발자들이 아키텍처에 따를 수 있게 커스텀 eslint 룰을 통해 가이드 제공
단방향 참조 아키텍처
효과적으로 커뮤니케이션 하기
문제 상황
- 팀원이 많아지면서 의견을 나누기 어려워짐
- 의사결정 과정이 투명하지 않고 히스토리가 남지 않음
- 비동기적 커뮤니케이션이 어려움
해결 접근법
- GitHub Discussion을 활용하여 의사결정 히스토리 기록
- 비동기적 커뮤니케이션 활성화로 시간대와 상관없이 의견 교환 가능
- 글로 정리하는 과정에서 생각을 더 명확히 표현하는 효과
GitHub Discussion 활용 예시
주제: eslint 활성화 룰 정의
참여자: 5명
결과: 기존의 일부 룰을 비활성화하기로 결정
웹소켓 모듈을 통한 DX 개선
문제 상황
- 웹소켓을 통해 서버와 통신할 때 특정 리소스를 해제하려면 이벤트를 보내어 관리해야 함
- 이러한 리소스 관리를 컴포넌트 단위에서 직접 처리하고 있었음
- 비즈니스 로직과 웹소켓 리소스 관리 로직이 혼재되어 코드 복잡성 증가
해결 접근법
- 리액트 쿼리와 유사한 설계를 가진 웹소켓 모듈 개발
- 컴포넌트는 웹소켓 모듈의 API만 사용하도록 구조 개선
- 리소스 관리 및 이벤트 처리 로직 중앙화
웹소켓 모듈 사용 예시
// 이전: 컴포넌트에서 리소스 관리
const ChatComponent = () => {
useEffect(() => {
// 채팅방 입장 이벤트 전송
socket.send(JSON.stringify({
type: 'JOIN_ROOM',
roomId: roomId
}));
return () => {
// 컴포넌트 언마운트 시 채팅방 퇴장 이벤트 전송
socket.send(JSON.stringify({
type: 'LEAVE_ROOM',
roomId: roomId
}));
};
}, [roomId]);
// 비즈니스 로직...
}
// 이후: 웹소켓 모듈 사용
const ChatComponent = () => {
// 리액트 쿼리와 유사한 API
const { data, isLoading } = useWebSocketResource({
resourceType: 'CHAT_ROOM',
resourceId: roomId,
// 리소스 관리는 모듈에서 자동으로 처리
});
// 비즈니스 로직에만 집중
}Tanstack Query를 통한 DX 개선
문제 상황
- 메모리 최적화를 위해 사용하지 않는 데이터는 캐시에서 제거해야 함
- 컴포넌트의 구조가 복잡해짐에 따라 컴포넌트 라이프사이클에 맞춰 캐시를 삭제하는 것이 어려워짐
해결 접근법
- Tasntack Query가 실험버전으로 Angular에서 사용 가능하다는 것을 알게 됨
- 테스트 프로젝트를 만들어 DX를 향상 시키도록 제안함
관련 블로그 글
Tanstack Query for Angular변경하기 쉬운 코드로 만들기
문제 상황
- API 인터페이스가 그대로 리덕스 인터페이스로 사용됨
- 서버 인터페이스의 변경이 클라이언트에 직접적으로 영향을 미침
- 커다란 인터페이스를 모든 컴포넌트에서 사용해 인터페이스가 점차 거대해짐
- 인터페이스 변경이 많은 컴포넌트에 직접적인 영향을 미쳐 코드 간 의존성 증가
해결 접근법
- API 인터페이스와 뷰 인터페이스 분리
- 각 컴포넌트에 필요한 값만 모아 컴포넌트별 props 인터페이스 생성
- 인터페이스 통째로 전달하지 않고 필요한 값만 선택적으로 전달
인터페이스 분리 예시
이전: 하나의 거대한 인터페이스
// 이전: 하나의 거대한 인터페이스
interface TaskData {
id: string;
title: string;
status_id: number;
assignee_ids: string[];
created_at: string;
watchers: string[];
comments: Array<{
id: string;
content: string;
user_id: string;
created_at: string;
}>;
// 20개 이상의 속성...
}
// 컴포넌트에 통째로 전달
<TaskCard task={taskData} />개선: 컴포넌트별 인터페이스
// 컴포넌트별 맞춤 인터페이스
interface TaskCardProps {
id: string;
title: string;
status: {
id: number;
name: string;
color: string;
};
assignees: Array<{
id: string;
name: string;
avatar: string;
}>;
// 필요한 속성만 포함
}
// 필요한 데이터만 매핑하여 전달
<TaskCard
id={task.id}
title={task.title}
status={getStatus(task.status_id)}
assignees={getAssignees(task.assignee_ids)}
/>RxJS의 메모리 누수 해결하기
문제 상황
- RxJS 사용 시 메모리 누수 발생
- 구독(subscription) 해제가 제대로 이루어지지 않음
- 장시간 사용 시 브라우저 성능 저하 및 크래시 발생
해결 접근법
- RxJS 스터디 및 세미나를 통한 팀 역량 강화
- 핫 옵저버블과 콜드 옵저버블의 정확한 구분
- 개발자 도구를 활용한 메모리 누수 지점 분석
- 구독 관리를 위한 표준 패턴 도입 및 적용
RxJS 메모리 누수 해결 패턴
// 이전: 구독 해제 누락
ngOnInit() {
this.dataService.getData().subscribe(data => {
this.data = data;
});
}
// 이후: 구독 관리 패턴 적용
private destroy$ = new Subject<void>();
ngOnInit() {
this.dataService.getData()
.pipe(takeUntil(this.destroy$))
.subscribe(data => {
this.data = data;
});
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}하드웨어 SDK 호환성 높이기
문제 상황
- 제조사마다 다른 SDK를 사용하여 영상 모듈 연동 필요
- RTSP 프로토콜 연동 시 호환성 문제 발생
- 표준 문서와 실제 구현 간 차이로 인한 오류
해결 접근법
- RTSP 프로토콜 표준 문서 분석
- 네트워크 패킷 분석 도구를 활용한 통신 흐름 파악
- 제조사별 SDK 차이점 분석 및 추상화 레이어 개발
RTSP 프로토콜 분석 결과
문제점: 제조사별 RTSP 구현 차이로 인한 연결 불안정
해결책: 표준 RTSP 클라이언트 개발 및 제조사별 특화 처리 로직 추가
결과: 연결 안정성 90% 이상 향상
보이지 않는 에러 개선하기
문제 상황
- 사용자가 보고하지 않는 숨겨진 에러로 인한 기술 부채 증가
- 프로덕션 환경에서만 발생하는 간헐적 오류 추적 어려움
- 소스맵 없이 난독화된 코드에서 에러 발생 위치 파악 불가
해결 접근법
- Sentry에 소스맵 연동으로 정확한 에러 발생 위치 파악
- 에러 발생 패턴 분석 및 우선순위화
- 잠재적 문제 코드 개선으로 사전 예방적 유지보수
Sentry 소스맵 연동 결과
TypeError: Cannot read property 'value' of undefined
at Object.t.updateTask (app.5f8e2d31.js:2:159223)
at e.value (app.5f8e2d31.js:2:159822)TypeError: Cannot read property 'value' of undefined
at TaskService.updateTask (services/task-service.ts:247)
at TaskComponent.saveChanges (components/task/task.component.ts:183)지속적인 성장
다양한 기술 스택을 익히고 발전하는 개발자
스크롤하여 더 알아보기
React
프론트엔드 개발에 관심을 가지기 시작한 때에 한국에서 리액트가 점차 인기를 얻기 시작했습니다. 이때부터 지속적으로 사용해보며 더 나은 개발 경험을 추구하고 있습니다.
SSR (Next.js, Remix, Qwik)
최근 SEO와 더불어 SSR에 대한 필요성이 부각되면서 Next.js와 같은 프레임워크가 대두되고 있습니다. 이를 계기로 SSR, SSG에 대한 학습을 진행하고 있습니다.
CSS in js for SSR
SSR 프레임워크가 많아지면서 기존 CSR에 동작하던 CSS in js 라이브러리들이 사용이 불가해짐에 따라 이를 극복 할 수 있는 방법에 대해 학습하고 있습니다.
프로토 버퍼를 통한 효율적인 통신
JSON을 통한 REST API는 개발이 쉽고 연동이 간편하지만, 바이너리보다 크기가 커서 많은 비동기 통신이 발생하는 앱에는 적합하지 않습니다. gRPC에서 사용하는 프로토버퍼를 이용해 서버와 웹소켓을 통해 통신함으로써 네트워크 효율성을 높이고 타입 안정성을 강화하는 방법을 연구하고 있습니다.
관련 블로그 글
아이디어
새로운 아이디어로 문제를 해결하는 오픈소스 프로젝트
스크롤하여 더 알아보기
React Context Query
React Context API를 사용할 때 발생하는 불필요한 리렌더링 문제를 해결하는 라이브러리를 개발하여 오픈소스로 공개했습니다.
함께 일해보세요
새로운 프로젝트나 협업 기회가 있으시다면 언제든지 연락주세요.
함께 멋진 것을 만들어 나갈 수 있기를 기대합니다.