type-safe하게 API 만들기

created:
last-updated:

현재 sqetch.club 프로젝트는 phoenix Socket , Channel 을 이용해서 Sveltekit app과 소켓통신을 하는 구조입니다. 프로젝트 poc단계에서 임시로 만들어둔 endpoint이긴 하지만 모든 타입들이 any 혹은 Object 타입인 상태로, 어떤 api에 어떤 payload data가 전송되는지 알 수가 없는 상태입니다.

phoenix에서 Socket api endpoint를 정의하고 이를 기반으로 trpc 혹은 openapi-ts로 필요한 데이터들의 type을 generate하고, Sveltekit에서 이 타입을 가져다 쓰도록 하면 graphql schema처럼 FE, BE 양쪽에서 single-source-of-truth 즉, 하나의 소스로부터 뽑아낸 type들을 기반으로 안정성이 보장된 api 연동개발을 할 수 있을 듯 합니다.

swagger 같은 솔루션으로 일부 자동화하거나, wiki 나 gitbook, docusaurus 등으로 별도의 문서로 관리해서 한 곳에서 api 스펙을 확인할 수 있는 방식이 있지만, 개발되는 실제 코드와 항상 싱크를 맞추고 관리하는 비용, 다른 플랫폼을 통하지 않고 코드단에서 좀 더 자동화해 기능 개발에 더 집중하고 싶은 니즈가 있는 상태입니다. 그렇게 찾아보던 여러 옵션 가운데 공부도 해볼겸 시도해볼만한 방법으로 openapi-ts가 끌려 리서치해보는 중에 있습니다.

현재 BE, FE가 각기 다른 레포를 쓰고 있어서 generate한 타입을 client측에서 가져올 방법이 필요해보입니다. 여기서부터 문제네요. 일단 떠올려본 방법은 3가지 정도입니다.

1. 현재 분리된 FE, BE 레포를 모노레포로 통합한다. 같은 레포 안에 있으니 직접 필요한 type 파일 경로에 접근할 수 있다.
2. BE에서 generate한 type을 expose할 private npm package를 만들고 FE에서 dependencies 모듈로 설치해서 사용한다.
3-1. git submodule를 사용해서 type 파일 및 코드를 각 FE, BE레포에서 공유할 수 있게 한다.
3-2. git submodule대신 git subtree를 사용한다.
1번은 다음과 같은 이유로 일단 제외
2번은 npm package 퍼블리시를 해볼 수 있는 좋은 공부 기회가 될수도?
3-1번 오 해볼만 한데?
# Create a new directory for the shared repository
mkdir shared-code
cd shared-code

# Initialize a new Git repository
git init

# Add a README file
echo "# Shared Code" > README.md

# Commit the initial files
git add .
git commit -m "Initial commit"

# Push to your remote repository
git remote add origin <remote-repository-url>
git push -u origin master
# Navigate to the client repository
cd path/to/client-repo

# Add the shared repository as a submodule
git submodule add <remote-repository-url> shared

# Commit the changes
git add .
git commit -m "Add shared code submodule"
// Import shared types
import { SharedType } from '../shared/types';
# Navigate to the server repository
cd path/to/server-repo

# Add the shared repository as a submodule
git submodule add <remote-repository-url> shared

# Commit the changes
git add .
git commit -m "Add shared code submodule"

(elixir에서 type을 사용할 수 있는지는 잘 모르겠음..타입스크립트의 타입을 어떤식으로 다루는지 언어의 특징을 잘 모르는 상태. BE 팀원분께 물어봐야겠음)

3-2 submodule과 subtree의 차이
git subtree add --prefix=<path> <repository-url> <branch> --squash

git subtree pull --prefix=<path> <repository-url> <branch> --squash

git subtree push --prefix=<path> <repository-url> <branch>
Feature Git Submodules Git Subtrees
Pros
Isolation Submodules maintain their own history, offering isolation from the parent repository. Subtrees merge the entire repository into the parent, but without full isolation.
Version Control Allows locking submodules to specific commits, ensuring consistency. Unified with the parent repository, no need to lock to specific commits.
Ease of Use Easier to manage than submodules, with no special commands for cloning/updating.
Cons
Complexity Managing submodules can be complex, requiring extra commands and considerations. Simpler but might lead to history bloat in the parent repository.
Separate Commits Changes to submodules require separate commits and pushes. All changes and history are unified with the parent repository.
History Bloat Minimal history impact on the parent repository. The parent repository's history can become large if the subtree has a long history.
Isolation Level Higher isolation between the parent and submodule. Less isolation, as subtree is more tightly integrated with the parent.

일단 이런 정도로 정리를 해봤습니다. 회사에서 graphql을 사용하면서 single endpoint, type-safe schema에 적응되어있었는데 REST, websocket api를 관리하고 개발하려니 역시 그냥 되는건 없구나 싶습니다. 개발환경 자체도 만들기 나름이고 이런 저런 솔루션과 툴을 찾아보다보니 사람들이 다 비슷한 고민을 하는구나 싶네요. 실제 type을 뽑아내는 과정은 다음 포스트에서 다뤄보겠습니다.


참고링크