
인생 첫 외주와 그 후기
패션 업계에서 일할 때 엄청 친해진 직장 동료 형이 있었다.
나는 회사를 퇴사하고 프로그래머로 직무를 전환했고, 그 형은 비슷한 도메인에서 자기만의 B2B 회사를 창업해 대표가 되었다. 우리는 관심사도 비슷하고 좋아하는 운동도 비슷해 꾸준히 연락하며 지냈다.
일주일 전쯤 그 형의 사무실로 초대를 받았다. 그리고 자신의 이야기를 전하며 내게 부탁을 하나 했는데...
1. 요구 사항
형이 겪고 있던 어려움은 신규 바이어에게 납품 가격이 산정되는 비즈니스 프로세스를 설명하기가 어렵다는 점이었다.
그리고 B2B 특성상 꾸준히 신규 바이어를 유치해야 하지만, 고객 입장에서는 상품이 국내 시장에서 얼마나 경쟁력 있는지 판단하기 어려워 비즈니스를 시작하기를 주저한다는 것이 문제였다.
고로 고객은 섣불리 오더를 넣기 힘들고, 형은 형대로 열심히 일하지만 성과가 없는 악순환이었다.
초기 아이디어
이를 위해 형이 나에게 요청한 사항은 이러했다.
- 신규 바이어를 위한 최종 국내 도착가 계산기
- 상품의 수익성를 파악할 수 있는 어떠한 도구
논의 후 확장된 요구 사항
형과 점심 식사를 하며 구체적으로 논의해 보니, 세부사항까지 고려한 기능은 다음과 같았다.
-
환율 자동 연동 및 최종 국내 도착가 계산기
- 유로와 달러 송금 시 당일 환율 정보 자동 연동
- 최소한의 정보를 기반으로 보수적인 최종 가격 산출
-
계산 내역 히스토리와 바잉 메리트 판단 도구
- 계산된 내역을 저장하고 목록에서 관리
- 국내 시장에서 바잉 메리트가 있는지를 가늠할 수 있는 간단한 평가 기능
2. 기술적 접근
이번 프로젝트는 간단하면서도 효율적인 기능 구현을 목표로 아래와 같은 기술 스택을 활용했다.
1. Next.js
- 서버 사이드 렌더링(SSR)을 적극적으로 활용하고자 했다.
Server Action을 통해 필요 로직을 서버에서 처리한 뒤, 로딩 없이 데이터를 내려주는 UX를 구현했다.- 이전 팀 프로젝트에서도 App Router 환경이었지만
api route방식만 사용해봤기에, 이번에는 공식 문서를 더 깊이 공부하며Server Component와Server Action을 활용했다. - 환율은 일정 주기로 업데이트되는 데이터이기에,
fetch함수에revalidate옵션을 적용하여 증분적 정적 재생성(ISR) 을 구현했다.
Shadcn/ui

Radix UI+Tailwind CSS기반의 Headless UI 라이브러리- 빠른 개발 속도와 높은 UI 완성도를 동시에 지향
- 직관적이며 간결한 UI를 통해 비즈니스 솔루션으로서의 신뢰성을 높이고자 했다.
- 처음에는 인라인 스타일링에 거부감이 있었지만, 실제로 사용해보니 생산성이 굉장히 뛰어났다.
Tailwind CSS도 관심이 생겼다.
Zustand
- 계산 내역을 저장하고 관리하기 위해 사용
- 간단한 로직이라 라이브러리가 과하다고 생각할 수도 있지만, 익숙함을 선택하여 생산성을 극대화했다.
zustand-persist플러그인을 사용해 세션 스토리지에 데이터를 저장하도록 구현해 새로고침 후에도 계산 기록이 유지되도록 했다.
Vercel

Next.js프로젝트 배포에 최적화된 플랫폼- 빌드 프로세스와 에러 로그 확인이 직관적이어서 모니터링이 편리
- 개인 프로젝트나 소규모 서비스에는 Vercel이 훌륭한 선택지라고 느꼈다.
3. 트러블 슈팅 (환율 조회 호러쇼)
환율 조회 기능을 구현하는 데에만 이틀이 걸렸다.
무료 환율 조회 API를 찾기 위해 구글링을 했지만, 제대로 동작하거나 내가 원하는 조건을 충족하는 API는 드물었다.
첫 번째 API 적용과 실패
처음 사용하고자 했던 환율 조회 API는 한국수출입은행 환율 Open API였다.
몇 가지 제약사항이 있는데,
-
1. 하루 최대 1,000번의 호출 제한
첫 번째 제약사항은 크게 문제가 되지 않았다.
유저 트래픽으로 인한 호출이 1,000번을 넘을 것 같지 않았고, 어차피 특정 시점을 기준으로 하루에 한 번만 갱신되는 데이터기 때문에 캐싱을 사용하면 충분히 해결 가능했다.
-
2. 영업일 11시 전후로 업데이트되는 데이터
두 번째 제약사항도 큰 어려움은 없었다. API를 호출하는 파라미터에 담을 날짜 값을 한 번 검증하는 로직만 추가하면됐다.
분기 처리를 통해
-> 오늘이 한국의 영업일인지를 판단
-> 영업일이 아니라면 가장 가까이 있는 영업일의 데이터
-> 영업일이라면 한국 시간으로 오전 11시를 지났는지 확인해주면 되었다.
-
3. 서버에서 직접 호출할 때 발생하는 인증 문제
세 번째 제약사항을 가벼이 넘겼던 것이 최대 패착이었다.
그도 그럴 것이, '서버로 호출 시 문제가 발생할 수도 있다'라는 문장을 '나에게는 절대 그럴 일이 없다'라고 자만해버려서 실제로 호출이 되지 않을 때 내가 문제를 겪고 있다고 생각하지 못했다.
로컬 개발 환경에서 서버로 api를 호출했을 때, SSL/TLS 정책 때문에,
UNABLE_TO_VERIFY_LEAF_SIGNATURE에러와 함께 응답을 거부했었는데, 이를 우회하고자 환경 변수에NODE_TLS_REJECT_UNAUTHORIZED = 0을 설정했었다. 결국 그렇게 구현한 코드는Vercel로 배포시 빌드 에러를 발생시켰다.배포 환경에서도 인증을 우회하고 API를 정상적으로 호출하기 위해
node-fetch라이브러리로agent설정을 통해 호출 시도를 해보았지만 모두 실패로 돌아갔다.이 때 비로소, 잊고 있던 세 번째 제약사항이 떠올랐고, API 담당자에게 문의한 결과 내부적으로 서버 IP를 등록해야만 API 호출을 정상적으로 할 수 있음을 알았다.
하지만
Vercel로 동적 IP를 사용하는 나의 배포 환경에서는 무료로 고정된 IP를 사용하는 것이 어려웠고, 결국 이 API를 통한 구현을 포기할 수밖에 없었다.
두 번째 API 적용과 성공
다시 나는 무료 환율 조회 API를 찾아나섰고, 이번에는 github 오픈 소스 API를 찾아냈다.
기존의 API와 비교했을 때의 특징은 다음과 같다.
-
장점
- 호출의 제한이 없음
- 암호화폐를 포함한 200개 이상의 화폐의 환율을 제공
- 한국 영업일 및 특정 시간의 기준이 없음
-
단점
- 국내 기준 환율과 100% 일치하지 않음
- 1KRW에 대한 1USD, 1EUR 같은 방식으로 제공하기에 추가 연산이 필요
- 송금 시 환율 스프레드를 따로 반영해야 함
결과적으로 이 API로 오늘의 환율을 불러와 최종 국내 도착가를 계산하는 기능을 완성했다.
최종 코드
'use server'; export async function fetchExchangeRates2() { try { const response = await fetch( 'https://cdn.jsdelivr.net/npm/@fawazahmed0/currency-api@latest/v1/currencies/eur.json', { next: { revalidate: 3600 }, // 1시간마다 갱신 } ); if (!response.ok) { throw new Error('Failed to fetch exchange rates'); } const data = (await response.json()) as ExchangeRateByEuro; const krwRate = data.eur.krw; // 1유로당 KRW const usdRate = data.eur.usd; // 1유로당 USD if (!krwRate || !usdRate) { throw new Error('Missing KRW or USD rate in the response'); } // 1% 송금 스프레드 가산 const krwWithSpread = krwRate * 1.01; // Calculate 1달러당 KRW const usdToKrwRate = krwWithSpread / usdRate; return { date: data.date, rates: { eurToKrw: parseFloat(krwWithSpread.toFixed(2)), // 1유로당 한화 금액 (송금 수수료 포함) usdToKrw: parseFloat(usdToKrwRate.toFixed(2)), // 1달러당 한화 금액 }, }; } catch (error) { console.error('Exchange rate fetch error:', error); return { date: '', rates: { eurToKrw: null, usdToKrw: null, }, }; } }
느낀점
1. 나는 구현이 빠른 편은 아니다
이번 외주를 진행하며, 해커톤에서 느꼈던 것처럼 나는 구현 속도보다 디테일에 집중하는 성향이 강하다는 것을 다시 확인했다.
과거에는 이를 단점으로 생각했던 적도 있었지만, 요구사항을 꼼꼼히 점검하고 기능의 디테일을 파고들면서 충분한 커뮤니케이션을 통해 프로덕트를 완성해나가는 것이 곧 나의 방식이다.
성공적인 프로덕트는 단순히 코드를 빠르게 짜는 것이 아니라, 사용자의 만족을 얼마나 충족시키는가에 달려 있다.
앞으로도 나만의 방식으로 문제를 분석하고, 품질과 디테일을 놓치지 않는 엔지니어로 성장하고 싶다.
2. 도메인이 중요하다
이번 프로젝트에서 도메인 지식의 중요성을 깊이 체감했다.
프로젝트의 요구사항이 형의 도메인(B2B 패션 비즈니스)에 대한 이해를 바탕으로 형성된 만큼, 내가 이전에 패션 업계에서 근무하며 쌓았던 경험이 프로젝트 진행 속도와 결과물에 긍정적인 영향을 미쳤다.
만약 이 프로젝트가 나에게 완전히 생소한 도메인에서 진행되었다면, 클라이언트의 문제를 이해하고 요구사항을 정의하는 데 더 많은 시간을 소비했을 것이다. 이를 통해 두 가지 인사이트를 얻었다.
1. 도메인에 대한 흥미가 개발의 몰입을 좌우한다.
아무리 개발 자체가 재미있어도, 내가 만든 프로덕트와 도메인에 대한 애정이 없으면 장기적으로 성취감을 느끼기 어렵다. 앞으로 직장이나 프로젝트를 선택할 때 단순히 기술 스택이나 조건만이 아니라 흥미로운 도메인을 선택하는 데도 신경을 쓰고자 한다.
2. 다양한 경험은 더 나은 엔지니어가 되는 발판이다.
우리의 인생이 원하는 대로만 흘러갈 순 없지 않은가? 내가 쌓아온 경험들이 새로운 환경에서 예기치 못한 도움을 줄 수 있다는 점에서, 가능한 많은 도전을 통해 다양한 경험을 쌓아야 한다.