/web38-Froxy

Gist clone과정을 폴짝! :frog: 건너뛰고 테스트

Primary LanguageTypeScript

Froxy

Portfolio UI Kit Cover
빠르고 간편하게 코드를 실행하세요 🐸

Froxy는 개구리를 뜻하는 ‘Frog’와 ‘Proxy’의 합성어로, 사용자가 직접 코드를 실행하지 않고도 결과를 빠르게 확인할 수 있는 서비스입니다. 🐸💻

Gist에서 코드를 복제하고 실행 환경을 설정하는 번거로움 없이, Froxy와 함께라면 폴짝! 뛰어넘어 간편하게 코드를 실행하고 결과를 확인할 수 있습니다. 다양한 기능을 통해 코드를 테스트하고 실행 결과를 즉시 확인해 보세요!


팀 노션 | 팀 피그마 | 팀 피그잼 | 개발 위키

목차

⭐️ 주제 선정 동기

부스트캠프 챌린지 과정에서는 매일 도전 과제를 수행하며, 동시에 그룹원들의 과제를 Gist를 통해 확인하고 피드백하는 시간을 가집니다.

이 과정에서 캠퍼들은 Gist 경로를 찾아 로컬 환경에 클론(clone)한 뒤 실행하는 일련의 과정을 반복하게 되며, 특히 익숙하지 않은 도구를 사용하면서 이를 수행할 때 여러 가지 개발 환경 문제를 겪게 됩니다.

Froxy는 이러한 반복적인 코드 확인과 실행 과정을 보다 간편하고 효율적으로 지원하기 위해 만들었습니다. Froxy는 캠퍼들이 실행 환경에서 발생하는 문제를 줄이고, 코드 리뷰와 피드백에 더 집중할 수 있는 환경을 제공합니다. 이를 통해 캠퍼들이 과제 수행과 코드 리뷰에 있어 생산성을 높이고, 학습 과정에 더 몰입할 수 있도록 돕는 것이 Froxy의 목표입니다.

image
검색하기 어려운 표
image
그룹원을 태그나 제목으로 검색

🐸 주요 기능

Gist 코드를 빠르게 게시하기

사용자의 Gist 코드를 쉽고 빠르게 게시할 수 있게 만들었습니다.

생성

다른 사람의 Gist 확인하기

다른 캠퍼가 올린 Gist를 빠르게 찾을 수 있습니다.

목록

Gist 코드를 빠르게 실행하기

코드의 입력값 제공해 빠르게 실행할 수 있습니다.

실행

🧑🏻‍💻 FE 기술적 도전

Froxy의 프론트엔드에서는 서버 데이터를 효과적으로 UI로 전달하는 것이 핵심 과제였습니다. 이를 위해 아래와 같은 기술적 도전에 집중했습니다.

QueryKey Factory로 QueryKey 구조화 하기

Effective QueryKey

  • Tanstack Query를 사용해 서버상태를 관리하고 있었기 때문에 각 도메인마다 구조화된 QueryKey가 필요했습니다.
  • 이를 위해, API 계층에서 사용하는 fetch 함수를 인자로 받아 QueryKey와 QueryFunction을 함께 동적으로 생성하는 QueryKey Factory를 구현했습니다.
  • 이를 통해 프론트 코드 전역에서 구조화된 QueryOption들을 선언적으로 재사용할 수 있었습니다.

계층화와 도메인 모델

나도 써 본 잘알려진 UI 패턴(프론트엔드 계층화, 도메인 모델 객체 활용)

  • 프론트엔드 코드의 유지보수성을 높이기 위해 계층화된 아키텍처를 도입했습니다.
  • 각각의 계층(API, Query, Hook, UI)를 통해 추가 요구사항이 생겼을 때 필요한 코드를 계층으로 분리해 독립적으로 개발할 수 있었습니다.
  • 또한 도메인 모델을 설계해 각각에 도메인에 필요한 비즈니스 로직을 캡슐화해 UI에서 불필요한 비즈니스 로직을 선언하지 않고 UI로직만 관리할 수 있도록 했습니다.
sequenceDiagram
    participant UI
    participant Hooks
    participant Query
    participant API

    UI->>Hooks: useCustomHook 호출
    Hooks->>Query: 데이터 쿼리 요청 (fetch or mutate)
    alt Cache HIT
        Query->>Query: 캐시 데이터 반환
        Query-->>Hooks: 도메인 Model과 쿼리 옵션 전달
    else Cache MISS
        Query->>API: HTTP 요청 전송 (Axios, fetch 등)
        API-->>Query: 응답 데이터 반환 후 Model로 래핑
        Query-->>Hooks: 도메인 Model과 쿼리 옵션 전달
    end
    Hooks-->>UI: 도메인 Model과 상태 전달
    UI->>UI: UI 업데이트
Loading

MSW로 개발 효율화하기

MSW로 개발 효율화하기

  • MSW를 이용해서 백엔드의 API 개발이 완료되지 않은 상태에서 API 호출과 관련된 시나리오를 테스트할 수 있었습니다.
  • MockRepository를 이용해 요청 조건에 따라 데이터를 동적으로 응답하도록 구현했습니다.
  • 이를 통해 인증이 필요한 요청에 대한 로직이나 tanstack query를 미리 적용해보고 테스트해볼 수 있어서 나중에 실제 API를 연결할 때 빠르게 진행할 수 있었습니다.

Suspense, ErrorBoundary로 Fallback 구현하기

Suspense, ErrorBoundary로 Fallback 구현하기

로딩 시 Fallback UI
로딩 시 Fallback UI
에러 시 Fallback UI
에러 시 Fallback UI
  • 리액트를 선언적으로 사용하기 위해서 로딩 상태는 Suspense가, 에러 상태는 ErrorBoundary가 관리하도록 역할을 분리하고자 했습니다.
  • Layout Shift 문제 방지와 사용자 경험 개선을 위해서 Skeleton UI를 사용해서 로딩 시 fallback UI를 구현했습니다.
  • ErrorBoundary와 tanstack query의 useQueryErrorResetBoundary() 훅을 이용해서 오류 발생 시 재시도가 가능하도록 에러 fallback UI를 구현했습니다.

🧑🏻‍💻 BE 기술적 도전

docker를 이용한 코드 실행

dockerode를 이용한 컨테이너 관리

  • 쉘을 이용한 방법보다는 nest에서 직접 docker를 관리하고자 했습니다.
  • git clone, image build보다 빠른 속도를 위해 컨테이너에 직접 파일을 parsing, 삽입하도록 구현했습니다.
  • 컨테이너와 소켓을 통해 입출력 결과를 반환할 수 있습니다.

queue & pool을 이용한 스케줄링

Redis-queue를 이용한 컨테이너 스케줄링

  • queue를 이용해 요청이 순차적으로 처리되도록 했습니다.
  • container pool을 직접 관리하여 리소스 사용률을 감소시켰습니다.
graph LR
    API[API 요청] -->|요청 추가| RedisQueue[Redis Queue]
        RedisQueue -->|작업 요청| DockerService[Docker 서비스]

    DockerService -->|컨테이너 요청| Pool[컨테이너 Pool]
        Pool -->|할당된 컨테이너| Container[컨테이너]
        Container -->|작업 실행| Task[js 실행]
        Task -->|결과 반환| Container
        Container -->|컨테이너 반납| Pool
        Container -->|작업 결과| Response[API 응답]
        DockerService -->|결과 반환| RedisQueue


Loading

Octokit 클라이언트 대신 직접 Gist API 모듈화

사용자 지정 Gist API 파싱 모듈

  • GitHub 공식 client Octokit 모듈 사용이 nest와의 호환성 문제로 인하여 REST API를 통해 모듈을 직접 구현했습니다.
  • 이때 type-safe한 환경을 만들기 위해서 응답과 요청에 대한 type 추론이 가능하도록 설계했습니다.
  • 이는TypeScript의 장점을 최대한 활용할 수 있도록 dto로 파싱하여 응답을 필터링하고 유효성 검사를 할 수 있게 되었습니다.

TypeORM을 통한 다대다 테이블 관리

typeORM 다대다 테이블 트러블 슈팅

  • update 함수를 쓰는 과정에서 다대다 관계의 데이터는 관계 테이블을 통해 연결되기 때문에, 직접 필드로 접근해 조건을 걸 수 없는 문제를 발견했습니다.
  • 다대다 관계를 사용하는 repository에서 update 사용 시, 쉽게 보이는 오류이며 save로 임시로 오류를 막았으나 find 및 save로 인한 원자성 해침을 막기 위한 방침이 필요했습니다.
  • 또한 tag 미사용 시 데이터를 자동으로 삭제하는 기능을 위해서, tag-lotus relation 테이블을 추가하여 one-many 관계로 분리하였습니다.

🔧 기술 스택

분류 기술 스택

공통

프론트엔드

백엔드

패키지 매니저

배포

협업

🔧 프로젝트 아키텍처

image

🔧 프로젝트 flow

flow

👥 팀원

김민우 이나경 김현지 문준호
김민우 이나경 김현지 문준호
FE FE BE BE