WIL - 2025.08 Week 4

|

이번 주는 회사에 출근하지 않고 재택으로 일했다. 개발팀장님이 출근할 때는 맞춰야 하지만, 크롤링 작업은 집에서 집중하는 것이 더 효율적이라고 판단했다.

크롤링 자동화를 위해 Electron, BrightData, Playwright로 개발을 시도했으나 실패했다. 시간 압박 속에서 조급하게 접근한 것이 원인이었던 것 같다. 다행히 동료의 코드로 수동 크롤링을 이어갈 수 있었다. 실패 과정에서 얻은 것도 있었다. 페이지 로딩 문제는 크롬 디버그 모드와 user-agent 설정으로 해결할 수 있다는 점을 알게 되었다. 다만 셀렉터 변동성이 많아 완전 자동화까지는 아직 어렵다고 느꼈다.

학습 측면에서는 패턴으로 익히고 설계로 완성하는 리액트를 읽기 시작했다. TDD의 레드-그린-리팩터 주기를 배우고, ACL(오류 방지 계층) 개념도 접했다. 회사에서 사용하던 services 구조가 바로 ACL의 일종임을 깨달았다. 데이터 변환을 한 곳에서만 처리해 뷰를 단순하게 유지한다는 점이 인상적이었다.

또, CS50 강의를 듣기 시작했다. 전구의 on/off를 1과 0으로 표현하는 이진수 개념을 배우고, 각 자리수가 2의 거듭제곱을 나타낸다는 것도 이해했다. 강의는 아직 초반이지만 강사의 열정 덕분에 흥미롭게 보고 있다.

추가로 BFE.dev에서 타입스크립트 문제를 하나씩 다시 풀고 있다.

한 주를 몰아 적다 보니 글이 길어지고 일기처럼 흘러간다. 하지만 지금은 배운 것을 기록하는 습관을 만드는 것이 더 중요하다고 생각한다. 언젠가는 짧고 단정한 기록으로 다듬어낼 수 있을 거라 믿는다.

WIL(Weekly I Learned)를 쓰게 되다.

|

하루는 금방 지나가고, 매일 무언가를 배우지만 금세 흘러가 버린다.
예전에 TIL을 썼을 땐 그 순간의 기록이 쌓이는 게 즐거웠다. 하지만 시간이 지나면서 어느새 숙제처럼 느껴지고, 결국 깃 잔디만 채우는 일로 변해버렸다. 기록의 의미가 흐려진 순간이었다.

요즘은 재택을 주로 하면서 출퇴근에 쓰던 시간을 아끼고 있다. 하지만 돌이켜보면 그 시간조차 온전히 잠으로만 흘려보내고 있었다. 편안하긴 했지만, 어딘가 아쉽다는 생각이 들었다.
그래서 다시 기록을 시작해 보기로 했다. 다만 이번에는 하루가 아니라 한 주 단위로.

억지로 모든 걸 남기려 하지 않을 거다. 그냥 내키는 대로 적고 싶은 만큼만 적을 거다. 살아보니 그게 나한테는 더 오래가는 방식이었다.

Weekly I Learned에서는 이번 주에 새로 알게 된 개념과 배운 내용, 회사에서의 업무 경험, 그리고 그 밖의 배움을 얻은 순간들을 함께 기록해둘 거다.
기록은 언젠가 되돌아보았을 때 내 발자취가 되어줄 거라 믿는다.

타입스크립트 분배 조건부 타입

|

BFE.dev 사이트의 타입스크립트 챌린지를 풀면서 새롭게 알게 된 개념들을 정리합니다.


TypeScript 조건부 타입에서 중요한 규칙 중 하나는
조건부 타입이 유니언 타입에 대해 자동으로 분배(distribute) 된다는 점이다.

📌 기본 형태

T extends U ? X : Y

여기서 T가 유니언 타입 (A | B | C)이라면,
조건부 타입은 각 멤버별로 나눠서 평가된다.

즉:

(A | B | C) extends U ? X : Y

는 다음처럼 분배된다.

(A extends U ? X : Y) |
(B extends U ? X : Y) |
(C extends U ? X : Y)

📌 분배를 막는 방법

분배가 항상 원하는 동작은 아닐것이다.
만약 T 전체가 E에 속하는지를 한 번에 체크하고 싶다면,
[T]처럼 튜플로 감싸주면 분배가 일어나지 않는다.

type NoDistribute<T, E> = [T] extends [E] ? never : T

✅ 정리

  • 조건부 타입에서 T가 유니언이면 멤버별로 분배된다. (반복문처럼 각 멤버를 순회하면서 조건을 적용한다)

  • 이를 분배 조건부 타입(Distributive Conditional Types) 이라고 한다.

  • 분배를 원하지 않을 때는 [T] extends [U]로 튜플 감싸기를 활용한다.

  • Exclude, Extract, NonNullable 등 여러 유틸리티 타입이 이 규칙을 기반으로 한다.

Cursor에서 Prettier Formatter 설정하기

|

매번 설정할 때마다 GPT한테 물어보는 게 귀찮아서, 이번에 아예 정리해두기로 했다.
Cursor(또는 VSCode 기반 에디터)에서 Prettier가 포맷터로 적용되지 않을 때 아래 순서대로 설정하면 된다.


✍️ 설정 방법

  1. Ctrl + Shift + P (Cmd + Shift + P on macOS) 누르기
    Preferences: Open Settings (UI) 입력 후 선택

  2. 상단 검색창에 default formatter 입력

  3. Editor: Default Formatter 항목 선택
    → 기본값이 None이라면, 드롭다운에서
    Prettier - Code formatter 또는 esbenp.prettier-vscode 선택


✅ 설정 완료 후

이제부터는 파일을 저장할 때 자동으로 Prettier 규칙에 맞게 포맷팅된다.
줄 정렬, 들여쓰기, 세미콜론 처리 등 깔끔하게 자동 적용된다.


💡 참고

추가로 아래 설정을 해두면 더 편리하다:

// .vscode/settings.json 또는 User Settings
{
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode"
}

결국, 내 코드가 문제였고요 – 401 디버깅 후기

|

이번 이슈는, 전혀 예상하지 못한 곳에서 시작됐다.
문제는 /store/regions와 같은 퍼블릭 API에서 계속해서 401 Unauthorized 에러가 발생했던 것이였다.

공식 문서를 수차례 확인해봐도 해당 엔드포인트는 인증 없이 접근 가능한 것으로 명시되어 있었고,
처음엔 API 키 설정, 서버 구성, 세일즈 채널, 헤더 누락, DB초기화 등 그럴듯한 원인들을 하나씩 의심하며 점검해나갔다.

단순한 설정 문제겠지 싶어서,
미들웨어 체인도 뜯어보고, 커스텀 인증 쪽 로직도 확인해보고, 다른 퍼블릭 API도 비교해봤지만…
어딘가 이상했다. 모든 게 정상이었기때문인데.. 정상인데 안 된다, 이게 제일 피곤한 케이스라 생각한다.

결국, 혹시나 하는 마음으로 예전에 만들어둔 커스텀 디지털 에셋 플러그인 코드를 열어봤다.
그리고 거기서 진짜 원인을 마주했다.

그 플러그인 안에, /store/* 경로에 인증 미들웨어가 덮어씌워져 있었던 거였다.
정확히 말하면, 내가 예전에 막 메두사 학습을 막 끝냈을 즈음, 생각 없이 커스텀을 시도하던 시절에 만들어둔 플러그인이었고, 그때는 그것이 전체 API 흐름에 어떤 영향을 줄지 전혀 고려하지 않았었다.

결과적으로, 나는 내가 만든 플러그인 때문에 퍼블릭 API 요청이 인증 오류로 막히는 상황을 스스로 만들어낸 셈이였다.

🛠 이번 문제를 해결하기까지 내가 취한 접근 방식은 다음과 같다:

1. 문서 재검토와 기본 설계 철학 복기

→ 메두사 공식 문서를 다시 훑으며 퍼블릭/프라이빗 API의 원칙 확인

2. API 인증 흐름 점검

→ 헤더, 키, 세일즈 채널 연결 여부, 인증 처리 방식 점검

3. 외부 의존성 제거 가정하 테스트

→ 커스텀 플러그인을 제외한 상태로 서버 실행 후 API 테스트

4. 플러그인 코드 내부 추적

→ /store/* 경로에 인증 미들웨어가 등록되어 있는 부분 확인

5. 플러그인 수정 및 재배포

→ 인증 설정을 제거한 뒤, 재배포 및 서버 반영

이번 경험은 단순히 버그 하나를 잡아낸 것 이상의 의미가 있었다.
디버깅 과정에서 기본으로 돌아가 되짚어보는 습관, 그리고 내가 만든 코드라도 항상 의심해보는 자세가 얼마나 중요한지를 다시금 깨달았다.

그리고 무엇보다도,
“어쩌면 문제의 원인은 내 코드일 수도 있다”는 생각을 너무 늦게 했다는 점에서,
배움은 늘 내 실수로부터 시작된다는 말이 떠올랐다.

다음에 또 비슷한 상황이 온다면,
이번보다는 조금 더 빠르게 자기 코드부터 의심하는 개발자가 되어 있기를 바라며, 이 기록을 남긴다..