April 2026

What is Ajax / Ajax API

Context

Shopify AJAX API 문서를 읽다가 “AJAX API”라는 명칭이 정확히 무엇을 뜻하는지 헷갈려서 찾아봤다.

What I Learned

AJAX 자체의 의미

AJAX = Asynchronous JavaScript And XML

페이지를 새로고침하지 않고 서버와 데이터를 주고받는 브라우저 패턴이다. XML은 이름에만 남아 있고, 현재는 JSON을 주로 사용한다. fetch, XMLHttpRequest 등이 AJAX를 구현하는 수단이다.

Shopify에서 말하는 “AJAX API”

일반 API와 달리 스토어 프론트엔드 JS에서 직접 호출하도록 열어둔 unauthenticated HTTP 엔드포인트 묶음이다. 이름에 AJAX가 붙은 건 역사적·용도적 이유에 가깝다.

일반 APICart AJAX API
사용 주체외부 시스템 / 백엔드스토어 테마 JS
인증필요불필요 (guest cart 가능)
상태 관리-세션 기반 cart state

흔한 오해

  • ❌ “AJAX API = 다른 종류의 API” → 그냥 HTTP 엔드포인트
  • ❌ “fetch랑 AJAX는 다른 기술” → fetch는 AJAX를 구현하는 방법 중 하나
  • ❌ “특별한 프로토콜” → 그냥 HTTP

Note

“AJAX API”라는 이름은 기술적 특이성보다 **설계 의도(프론트엔드에서 바로 쓸 수 있는 unauthenticated 엔드포인트)**를 나타내는 표현에 가깝다.

aiohttp ClientTimeout: 단계별 타임아웃 설정

Context

이미지 다운로드 로직에서 SoftTimeout 에러가 연쇄 발생했다. 원인을 추적해보니 dead host에 대해 session.get(url)에 timeout이 없어서, OS 레벨 TCP connect timeout(~30s)이 3번 발동하는 구조였다.

3 × 30s (TCP timeout) + 6s (retry sleep) ≈ 96s / URL

dead host 하나가 96초짜리 병목이 되어 전체 세션을 잡아먹고 있었다.

What I Learned

aiohttp.ClientTimeouttotal 하나만 있는 게 아니라 연결 단계별로 따로 걸 수 있다.

필드적용 범위
total전체 작업 (연결 + 요청 + 응답 읽기) 최대 시간
connect새 연결 수립 또는 풀에서 여유 연결 대기 최대 시간
sock_connect실제 소켓 연결(peer 연결) 최대 시간
sock_read응답 데이터를 peer로부터 읽는 사이 최대 대기 시간

기본값:

aiohttp.ClientTimeout(
    total=5*60,       # 5분
    connect=None,     # 제한 없음
    sock_connect=None,
    sock_read=None,
)

dead host 문제처럼 연결 자체가 안 되는 케이스total만으로는 대응이 늦다. sock_connect를 짧게 걸면 연결 시도 단계에서 일찍 끊을 수 있다.

timeout = aiohttp.ClientTimeout(
    total=10,
    sock_connect=3,  # 연결 자체가 3초 안에 안 되면 포기
    sock_read=5,
)
async with session.get(url, timeout=timeout) as resp:
    ...

Note

timeout=None(기본값)으로 놔두면 OS 레벨 TCP timeout에 의존하게 된다. Linux 기준 약 30s인데, retry까지 붙으면 한 URL당 수십-수백 초가 소요될 수 있다. 외부 리소스를 fetch하는 코드엔 반드시 명시적 timeout을 걸자.

Athena Partition과 Partition Projection

Context

S3에 적재된 raw event data를 Athena로 쿼리하려다가 partitioning이 안 된 테이블이라는 걸 알게 됐다. 쿼리 효율을 높이기 위해 partition projection을 적용했다.

What I Learned

1. Partition vs Index — 같은 범주가 아니다

항목PartitionIndex
목적읽을 데이터 양 자체를 줄임데이터 찾는 속도를 빠르게
데이터 위치물리적으로 분리됨그대로 있음
작동 방식특정 partition만 스캔전체 중에서 빠르게 lookup
효과I/O 감소 (big data에서 핵심)CPU/lookup 최적화
Athena/S3매우 중요 (거의 필수)없음

Partition은 데이터를 물리적으로 나눠 저장하고, 쿼리 시 일부만 읽도록 만드는 구조다.

s3://bucket/
  ├── event_date=2026-04-25/
  ├── event_date=2026-04-26/
  └── event_date=2026-04-27/
WHERE event_date = '2026-04-27'
-- → 해당 폴더만 읽음 (partition pruning)

Athena는 index가 없다. 성능은 오직 얼마나 적은 S3 데이터를 읽느냐에 달려 있다.

partition 있음 → 필요한 파일만 읽음 → 빠름 + 저렴
partition 없음 → 전체 스캔 → 느림 + 비쌈

2. Partition Projection

partition 메타데이터를 실제로 저장하지 않고, 규칙으로 계산해서 사용하는 방식

전통적인 방식은 S3에 데이터를 올린 뒤 Glue Crawler를 돌리거나 ALTER TABLE ADD PARTITION을 실행해 메타데이터를 별도로 등록해야 한다.

Partition Projection은 DDL에 규칙만 정의하면 Athena가 경로를 직접 계산한다.

TBLPROPERTIES (
  'projection.enabled'='true',
  'projection.event_date.type'='date',
  'projection.event_date.range'='2026-01-01,NOW',
  'storage.location.template'='s3://bucket/${event_date}/'
)

쿼리 실행 시 내부 동작:

WHERE event_date = '2026-04-27'
→ template에 대입 → s3://bucket/2026-04-27/
→ 이 경로만 읽음 (metadata lookup 없음)

partnerid / eventtype / event_date 같이 partition 조합이 많을수록 projection이 유리하다. Crawler 없이 관리할 수 있다.

3. 실무 주의사항

반드시 partition 조건을 WHERE에 명시해야 한다.

-- ❌ 위험 — 모든 partition 조합을 스캔 시도 → 느림 / 비용 폭발
SELECT * FROM my_table LIMIT 100;

-- ✅ 올바른 방식
SELECT * FROM my_table
WHERE partnerid = 'A'
  AND event_date = '2026-04-27';

injected 타입으로 설정된 partition 컬럼은 Athena가 가능한 값 목록을 모른다. 쿼리에서 반드시 직접 지정해야 한다.

Note

Projection은 partition 수가 많아질수록 강력하다. 반대로 WHERE에 partition 조건을 빠뜨리면 전체 스캔보다 더 나쁜 결과가 나올 수 있으니 팀 내 쿼리 가이드를 명확히 해두는 게 좋다.

Smoke Testing: 전체를 돌리기 전에 최소한만 검증하기

Context

여러 레이어를 활용하는 파이프라인을 설계하고 구현하는 과정에서 “smoke test부터 돌려보자”는 말이 나와서 정확한 의미를 확인했다.

What I Learned

Smoke testing이란 시스템의 핵심 기능이 기본적으로 동작하는지 빠르게 확인하는 가벼운 테스트다. “이 빌드를 더 깊이 테스트할 가치가 있는가?”를 먼저 판단하는 게 목적이다.

어원은 전자기기 분야에서 왔다 — 회로 기판에 전원을 처음 연결했을 때 연기(smoke)가 나지 않으면 기본적으로 괜찮다고 보는 데서 유래했다.

특징

  • 전체 테스트 스위트를 돌리기 전 최소한의 통과 기준만 확인
  • 빠르게 실행되고, 실패 시 즉시 멈춤
  • “작동은 하는가?”에만 집중 — 세부 동작의 정확성은 다음 단계

파이프라인에서의 활용 예시

[smoke test]
- 각 레이어가 오류 없이 실행되는가?
- 입력/출력 형식이 맞는가?
- 빈 결과나 예외 없이 끝까지 흐르는가?

Note

복잡한 파이프라인일수록 smoke test가 유용하다. 전체를 완성하기 전에 각 레이어가 연결되는 골격만 먼저 통과시켜보면, 설계 단계의 문제를 일찍 발견할 수 있다.

Monkey Patching: 런타임에 함수를 덮어씌우는 기법

Context

유저 이벤트 로그를 심다가, Target의 필터 UI가 URL을 바꿀 때 브라우저가 아무 이벤트도 발생시키지 않는다는 걸 알게 됐다. popstate는 뒤로가기/앞으로가기에서만 발생하고, 사이트가 직접 pushState / replaceState로 URL을 갱신할 때는 표준 이벤트가 없다.

그래서 monkey patching으로 직접 훅을 걸었다.

What I Learned

Monkey patching이란 런타임에 이미 정의된 객체/함수의 동작을 외부에서 덮어쓰거나 확장하는 기법이다. 원본 소스 코드는 건드리지 않고, 실행 중인 메모리상의 객체를 바꿔치기한다.

실제 적용 코드

['pushState', 'replaceState'].forEach((m) => {
    const original = history[m];
    history[m] = function (...args) {
        const ret = original.apply(this, args); // 원본 반드시 호출
        window.dispatchEvent(new Event('locationchange'));
        return ret;
    };
});

브라우저 내장 history.pushState를 우리 함수로 교체한다. 이후 누가 호출하든 우리 버전이 실행되고, 커스텀 이벤트를 발생시켜 URL 변화를 감지할 수 있다.

언제 유용한가

  • 브라우저/서드파티 API에 훅을 걸 때 — 수정 불가능한 내장 API를 덮어씌우는 것은 가능
  • 긴급 버그 패치 — upstream 수정 전 임시 우회
  • 계측/모니터링 — Sentry, DataDog 같은 APM 도구들이 fetch, XMLHttpRequest, console.error 등을 몽키 패칭해서 에러/요청을 자동 수집

주의점

위험내용
전역 영향한 번 덮어씌우면 페이지 전체 모든 호출이 영향받음
업스트림 변경원본 API 시그니처가 바뀌면 패치가 조용히 고장날 수 있음
디버깅 난이도스택트레이스에 패치된 함수가 찍혀 원인 추적이 어려워짐
원본 호출 누락original.apply(this, args)를 빼먹으면 원래 동작이 사라짐

Note

가능하면 표준 이벤트/옵저버를 먼저 찾고, 없을 때 최후 수단으로 쓰는 게 좋다. 이번 케이스는 “브라우저가 history 변화에 대한 표준 이벤트를 제공하지 않는다”는 명확한 이유가 있어서 정당화되는 사례다.

Clique: 그래프 이론 개념을 데이터 분석에 활용하기

Context

여러 데이터 소스 결과를 합쳐서 n~m개로 구성된 상품 세트를 추출하는 파이프라인을 설계하다가, clique를 활용하는 방법을 써봤다. CS 교재에서만 보던 개념이 실전 데이터 분석에 이렇게 쓰일 수 있다는 게 신기했다.

What I Learned

Clique란 그래프에서 모든 노드 쌍이 서로 연결된 완전 부분 그래프(complete subgraph)다. 즉, 클리크 안의 모든 노드는 서로 edge로 이어져 있다.

A - B
|\ /|
| X |   → {A, B, C, D}가 clique
|/ \|
C - D

상품 세트 추출에 적용한 파이프라인

  1. Pair 추출 — 각 데이터 소스(예: 구매 이력, lift 분석 등)에서 함께 등장하는 상품 쌍(pair)을 추출
  2. 그래프 구성 — 강한 연관성을 가진 pair를 edge로 연결해 그래프 생성
  3. k-clique 탐색 — 크기 k인 clique를 찾아 “서로 강하게 연관된 상품 묶음” 후보 도출
  4. Hybrid 검증 — clique 후보를 실데이터로 검증해 최종 세트 확정

왜 clique인가

  • 단순히 “같이 자주 팔린 상품”을 모으면 약한 연결이 섞여들 수 있음
  • clique는 그룹 내 모든 쌍이 강하게 연결되어 있음을 보장 → 세트로서의 응집도가 높음
  • n~m개 범위의 세트를 추출할 때 k값을 조절해 크기 조건을 자연스럽게 적용할 수 있음

Note

그래프 이론 알고리즘이 CS 이론에서만 쓰이는 게 아니라 실제 데이터 파이프라인 설계에도 유용하게 쓰인다. clique 탐색은 NP-hard 문제라 데이터가 커지면 근사 알고리즘이 필요하지만, 실전에서는 후보를 먼저 좁혀두고 검증하는 Hybrid 방식으로 충분히 쓸 만하다.

Ingress vs Egress: 데이터 흐름 방향 용어

Context

파이프라인 플로우 문서를 정리하다가 ingress / egress를 정확한 의미 없이 쓰고 있었다는 걸 알게 됐다.

What I Learned

네트워킹/클라우드 맥락에서 기준점(시스템 또는 네트워크)을 중심으로 데이터가 들어오냐 나가냐의 차이다.

용어방향예시
Ingress외부 → 내부사용자 요청, 외부 트래픽, 웹훅 수신
Egress내부 → 외부파일 다운로드, 타 클라우드로 데이터 전송

Note

클라우드 비용 맥락에서 egress는 특히 중요하다. AWS 등 대부분의 클라우드 제공사는 데이터가 나갈 때(egress)만 비용을 청구하고, 들어오는 건(ingress) 무료인 경우가 많다.

dig 명령어로 DNS 경로 추적하기

Context

웹훅 처리 구조를 파악하다가 특정 도메인이 실제로 어느 서버를 가리키는지 확인해야 했다. Route53에서 도메인이 보이지 않아 DNS 경로를 직접 추적했다.

What I Learned

dig (Domain Information Groper)은 터미널에서 DNS 조회 결과를 직접 확인하는 CLI 도구다. macOS/Linux에 기본 설치돼 있다.

기본 사용법

# 도메인이 가리키는 IP 또는 CNAME 확인
dig +short global.example.com

# 이 도메인의 권한 있는 네임서버 확인
dig NS example.com +short

출력 해석:

  • A 레코드 → IP 주소
  • CNAME 레코드xxx.elb.amazonaws.com 같은 형태면 AWS ALB로 연결된 것

실제 확인 결과 예시

$ dig +short global.example.com
my-app-1234567890.ap-northeast-2.elb.amazonaws.com.
x.x.x.x
x.x.x.x

$ dig NS example.com +short
ns.somedns.co.kr.
ns1.somedns.co.kr.

→ CNAME이 .elb.amazonaws.com이면 AWS ALB 경유 확정 → 네임서버에 awsdns가 없으면 Route53이 아닌 외부 DNS (Gabia, Cloudflare 등)에서 관리 중

확인된 웹훅 ingress 경로

global.example.com
   │  (외부 DNS의 CNAME 레코드)

my-app-xxxx.ap-northeast-2.elb.amazonaws.com
   │  (AWS Application Load Balancer)

Elastic Beanstalk environment (EC2 instances)

Route53에 도메인이 안 보이는 이유 후보

  1. AWS 계정이 다름 — 다른 계정의 Route53에 등록돼 있을 수 있음
  2. 외부 DNS 사용 — Cloudflare, Gabia 등 외부 레지스트라에 직접 레코드가 등록된 경우
  3. IAM 권한 부족 — 볼 수 있는 권한이 없을 수 있음

Note

dig NS <도메인> +short으로 네임서버를 먼저 확인하면 어디서 DNS를 관리하는지 바로 알 수 있다. Route53에서 못 찾겠으면 외부 DNS부터 의심하자.

Lift: 두 상품의 연관성을 수치로 표현하는 지표

Context

Shopify 스토어 cross-sell 최적화를 고민하다가 “lift가 높다”는 표현이 나와서 정확한 의미를 찾아봤다.

What I Learned

Lift는 장바구니 분석(Market Basket Analysis)에서 쓰는 지표로, 두 상품이 우연히 같이 팔릴 확률 대비 실제로 같이 팔린 비율이다.

Lift(A, B) = P(A ∩ B) / (P(A) × P(B))
Lift 값의미
= 1두 상품이 서로 독립적 (우연 수준)
> 1기대보다 더 자주 함께 구매됨 → 연관성 있음
< 1기대보다 덜 함께 구매됨 → 오히려 상충

실제 예시 — 상품 A + 상품 B (lift 17.6x)

  • A 구매 확률: 36/634 = 5.7%
  • B 구매 확률: 16/634 = 2.5%
  • 우연히 같이 담길 확률: 5.7% × 2.5% = 0.14% → 634건 중 약 0.9건 예상
  • 실제 함께 구매: 16건 → 기대치의 17.6배

Note

Lift가 높을수록 “이 상품을 산 사람은 저 상품도 살 가능성이 우연이 아니라 진짜 높다”는 뜻. cross-sell 추천 우선순위를 정할 때 단순 동시 구매 횟수가 아니라 lift로 판단해야 작은 카테고리의 강한 연관성을 놓치지 않는다.

Desktop bounce rate 이상 진단 프로세스

Context

Shopify 스토어 지표 분석 중 전체 bounce rate는 높은데 mobile은 괜찮고 desktop만 유독 안 좋은 케이스를 만났다.

What I Learned

mobile/desktop 지표 격차가 크면 진입 페이지 자체가 다를 수 있다.

top landing pages를 device별로 분리해서 보니 주요 진입 페이지가 완전히 달랐고, desktop 주요 진입 페이지에서 트래픽이 대거 이탈하고 있다는 걸 확인했다.

진단 흐름

  1. 전체 지표 이상 감지 (bounce rate 높음, conversion funnel 극악)
  2. device별 분리 → desktop만 문제
  3. top landing pages by session count를 device별로 분리 → 진입 페이지가 다름을 발견
  4. 문제 페이지 특정 → desktop top landing page에서 트래픽 누수 확인
  5. 봇 트래픽 여부 검증 → DoD(Day-over-Day) 트래픽 패턴 확인
    • 일정하면 봇 의심, spike & tail 형태면 외부 링크 공유 가능성 높음
  6. 원인 가설 수립 → 해당 페이지에 CRO action 적용 후 기간 테스트 계획

Note

트래픽 이상을 봤을 때 전체 지표만 보지 말고 device / landing page 축으로 쪼개는 것이 진단의 시작. DoD 패턴은 봇 트래픽을 빠르게 필터링하는 데 유용하다.

CSS shrink-to-fit: 브라우저가 너비를 콘텐츠에 맞추는 방식

Context

position: absolute + right: 320px인 컴포넌트 안의 Lottie 애니메이션이 가로로 찌그러지는 버그를 디버깅하다가 shrink-to-fit 개념을 제대로 이해하게 됐다.

What I Learned

블록 요소는 기본적으로 부모 너비를 가득 채우지만, 특정 상황에서는 브라우저가 반대로 너비를 콘텐츠에 맞춰 줄이는(shrink-to-fit) 방식으로 계산한다.

shrink-to-fit이 발동하는 조건:

  • position: absolute이면서 left/right 중 하나가 auto이고 width: auto인 경우
  • display: inline-block
  • float: left / right
  • flex 아이템 (일부 경우)

이때 너비 계산 공식:

width = min(max-content, max(min-content, available))
  • max-content: 줄바꿈 없이 한 줄로 폈을 때의 자연스러운 너비
  • min-content: 쪼갤 수 없는 가장 긴 단위(단어, 이미지 등)의 너비
  • available: 부모 너비에서 여백·positioning 제약을 뺀 가용 폭

실제 케이스에 대입:

컨테이너: 552px
right: 320px → available = 552 - 320 = 232px
max-content(자연 폭) ≈ 280px

→ width = min(280, max(min-content, 232)) = 232px

박스가 280px이고 싶은데 232px로 클램프 → 내부 flex 자식들이 flex-shrink: 1 기본값으로 비율 축소 → Lottie가 찌그러짐.

해결: min-width: max-content

min-width는 shrink-to-fit 결과에 하한선을 건다.

min-width: max-content; /* = 280px */

공식이 232를 뱉어도 최종 width는 max(232, 280) = 280이 되어 자연 폭 유지.

Note

transform은 레이아웃이 끝난 뒤의 시각 변환이라 shrink-to-fit을 막지 못한다. transform: scale(0.8)로 줄이고 싶다면, 레이아웃 단계에서 먼저 min-width로 너비를 고정해야 한다.

cURL -v flag

Context

curl 요청 보내는데 계속 4xx 뜨고 response가 안 와서

What I Learned

curl -v에서 -v는 verbose mode. 요청/응답의 “디버깅용 상세 로그”를 전부 출력.

요청 정보 어떤 URL로 요청하는지 어떤 헤더가 붙는지

> GET /api/test HTTP/1.1
> Host: example.com
> User-Agent: curl/8.x

응답 헤더

< HTTP/1.1 200 OK
< Content-Type: application/json

등 low level 출력

Note

March 2026

SOCKS5 Proxy IP 우회하기

Context

지역 차단되어 있는 웹사이트 접근이 필요했는데, 무료 VPN들은 다 차단되어 있음. Claude가 이미 띄워져 있는 미국 region EC2를 활용하는 SOCKS proxy 사용을 제안함.

What I Learned

SOCKS (Socket Secure) is a network protocol for routing client to server requests made through a proxy server.

SOCKS proxies (unlike HTTP proxies) are flexible and protocol-agnostic. They simply pass along the data packets without necessarily understanding the underlying protocol (like HTTP, HTTPS, FTP, etc.). It operates at a low level.

SOCKS proxies simply relay data w/o analyzing it - resulting better performance & anonymity - and does not encrypt data as a trade-off.

Both SOCKS5 proxies and VPNs mask IP addresses, but they function differently.

  • A VPN works at the operating system level, encrypting and routing all internet traffic from your device through the VPN server.
  • A SOCKS5 proxy doesn’t offer encryption and operates on the application level- generally faster and more lightweight.

아래처럼 터미널에서 백그라운드로 터널을 연결하고 FoxyProxy Chrome extension을 활용하여 SOCKS5 proxy를 설정했다.

Code

ssh -i "pem-key-for-us-region-ec2.pem" -D 1080 -N -q ec2-user@ec2-address.us-region.compute.amazonaws.com

Note

접근 성공!

What is less

Context

vim으로 서버 로그 보기에 지쳤다. 요즘은 less를 활용하고 있음.

What I Learned

The less command in Linux is used to view the contents of a file one page at a time without opening it in an editor, making it ideal for reading large files efficiently.

터미널에서 파일을 열어서 수정하는 editor도 아니고, 전부 출력하는 cat과도 다름. 필요한 부분만 스크롤하면서 보는 viewer/pager.

자주 쓰는 사용법:

검색

  • /keyword → 아래 방향 검색
  • ?keyword → 위 방향 검색
  • n → 다음 검색 결과
  • N → 이전 검색 결과

navigation

  • Space → 한 페이지 아래
  • b → 한 페이지 위
  • Enter → 한 줄 아래
  • y → 한 줄 위
  • d → 반 페이지 아래
  • u → 반 페이지 위

and more…

  • :123 → 123번째 줄로 이동
  • 50% → 파일의 50% 지점으로 이동
  • &keyword → 해당 키워드 포함된 줄만 보여줌 (간단 필터)
  • &pattern1|pattern2 → OR 조건도 가능

Note

서버 로그 정리를 해야 한다 loki 연동 필요

Computer Use Agent

Context

우리의 새로운 파이프라인이 CUA를 핵심 tool로 활용한다. 이 파이프라인에 손대는 겸, 개념 확인

What I Learned

CUAs use the vision capabilities of multimodal models to interpret what’s happening on the screen, and they combine that with an AI agent framework that can plan tasks and reason out what to do next.

주로 visual screenshot, DOM, user instruction 을 활용하여 미션을 수행하는 agent loop system 에 사용.

아래는 Claude code computer tool usage docs example

Code

curl https://api.anthropic.com/v1/messages \
  -H "content-type: application/json" \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "anthropic-beta: computer-use-2025-11-24" \
  -d '{
    "model": "claude-opus-4-6",
    "max_tokens": 1024,
    "tools": [
      {
        "type": "computer_20251124",
        "name": "computer",
        "display_width_px": 1024,
        "display_height_px": 768,
        "display_number": 1
      },
      {
        "type": "text_editor_20250728",
        "name": "str_replace_based_edit_tool"
      },
      {
        "type": "bash_20250124",
        "name": "bash"
      }
    ],
    "messages": [
      {
        "role": "user",
        "content": "Save a picture of a cat to my desktop."
      }
    ]
  }'

Note

다음 태스크 작업할 때는 직접 써봐야지

HTTP 422 Status Code

Context

API 구현해놓고 테스트하는데 422 에러

What I Learned

HTTP 422: “Unprocessable Entity”

Client error - the server understood the content type of the request content, and the syntax of the request content was correct, but it was unable to process the contained instructions.

대표 발생 케이스:

  • validation failure (e.g., not an email)
  • correct type but invalid
  • correct schema but required field missing

Note

내 경우 데이터의 id가 varchar 대신 bigint 여서 number 로 id 넘기다가 overflow 되어 버린 게 원인이었다

DB table에 id 만들 때 왜 bigint로 넣은거야 ??

Invisible ASCII 0x08 Control Character

Context

Postman으로 API 쏴서 테스트 하는데, 분명 맞는 URL인데 400 에러가 발생. 알고보니 invisible character가 끼어 있었음.

What I Learned

%08은 백스페이스 문자(ASCII 0x08) 을 비롯한 control character or non-printing character (NPC) 가 ASCII에 33개 있음.

macos 기본 에디터에서는 보이지 않음.

Postman > view > show postman console 에서 구체적인 network 요청 확인 가능

SHOW CREATE TABLE로 스키마 빠르게 파악하기

Context

DB read MCP가 작동을 안 해서 직접 스키마를 파악해야 했음

What I Learned

MySQL에서 해당 테이블의 “생성 DDL”을 그대로 출력하는 명령

테이블이 어떤 구조로 만들어졌는지를 보여줌

단순 스키마 조회(DESCRIBE)보다 훨씬 상세함

Code

SHOW CREATE TABLE table_name;

Note

테이블 구조를 다른 환경에 재현하거나 공유할 때도 유용.

CloudFront 배포 JS 파일에 CORS 헤더 추가하기

Context

cloudFront로 서빙 중인 dev 환경 프로덕트가 갑자기 브라우저에서 로딩이 안 됨

What I Learned

확인해 보니 JS 파일에 CORS 헤더가 없어서 발생한 문제.

CORS 헤더는 HTML이 아니라 JS 파일 응답(Response) 자체에 붙어야 함. CloudFront에서 JS를 서빙할 때 응답 헤더에 Access-Control-Allow-Origin: *가 없으면 브라우저가 스크립트 로드를 차단.

-> 해결 방법: CloudFront > distribution > behaviors > Response Headers Policy 설정

Singleton pattern in python

Context

python 레포 피쳐 개발하다가, 너무 오랜만에 이 개념을 봐서 복습.

What I Learned

The Singleton Pattern ensures a class has only one instance throughout a program and provides a global access point.


It is commonly used for managing shared resources like databases, logging systems or file managers.

All Python modules are singletons by default. Any variables or functions defined in a module are shared across imports.

Code

class Single:
    instance = None

    def __new__(cls):
        if cls.instance is None:
            cls.instance = super().__new__(cls)
        return cls.instance


a = Single()
b = Single()

print(a is b)

Note

put the definition to the name and try not to forget

aiohttp HTTPS 연결 실패-TLS cipher 협상 문제

Context

특정 서버에 접속해서 이미지 url을 다운받는 로직에서 반복적으로 연결 실패. error msg: Connection reset by peer

What I Learned

A cipher suite is a set of algorithms that help secure a network connection. Suites typically use Transport Layer Security (TLS) or its deprecated predecessor Secure Socket Layer (SSL) as their protocol.


TLS에는 3가지 암호를 협상함: 키 교환 (Key Exchange), 대칭 암호 (Bulk Encryption), 무결성 (MAC / AEAD)

원인 및 해결

  • Python의 기본 aiohttp의 디폴트 ssl context 에서 구형 cipher 차단.
  • 문제 생긴 서버의 cipher suite 확인해보니 구형
  • 이로 인해 TLS handshake 단계에서 cipher 협상이 실패하고 연결이 reset
  • 해결: 연결 실패 시 fallback으로 SSLContext 에서 보안레벨을 낮춰 레거시 cipher를 허용

Code

ctx = ssl.create_default_context()
ctx.set_ciphers("DEFAULT@SECLEVEL=1")

Note

이전에 있었던 SSL 연결 문제랑은 원인이 달랐음. 이전 문제는 인증서 체인 문제로 우리가 해결할 수 없었지만, 이번 이슈는 fallback 로직을 통해 접근 가능했음. 문제 원인 파악을 확실히 할 것.

결합 운모

Context

일단해 중국어 왕초보 2강

What I Learned

기본 운모 a o e i u ü 가 조합되거나 -n, -ng 받침이 붙은 결합 운모들

  • a : ai / ao / an / ang
  • o : ou / ong
  • e : ei / en / eng / er
  • i : ia / ie / iao / i(o)u / ian / iang / iong / in / ing
  • u : ua / uo / uai / uan / uang / u(e)i / u(e)n / ueng
  • ü : üan / üe / ün

Note

매우 많고 어렵다. 이제 겨우 발음 다 배웠다!

best-effort practice meaning

Context

데이터 처리 에러 확인하다가: dev 환경의 에러가 prod 환경 데이터 처리를 실패시키고 있었음

What I Learned

이럴 때 쓰는 오퍼레이션을 best-effort 라는 용어로 표현한다

Best-effort operation = non-guaranteed attempt (networking context에서 많이 사용 - packets may be dropped)

  • no strict guarantee (fail or incomplete may happen)
  • failure tolerated (dev 처리 실패해도 prod operation crash 하면 안 되지)
  • for non-critical tasks (e.g., logging, metrics, cleanup jobs)
  • performance prioritized over reliability

Note

guarantees are expensive, so sometimes unnecessary.

이거랑 별개로 이 cross-env crash bug issue 는 너무나 취약한 설계라 당황스러움 (오래된 레거시 코드임). best-effort 로 수정 완료.

ps -ef | grep python command

Context

서버 ssh 접속해서 dev server 실제로 돌아가고 있는지 체크하다가 자동반사적으로 쓰던 명령어의 의미 확인

What I Learned

  • ps : process status. 기본은 현재 터미널에서 실행 중인 프로세스
  • -e : all processes. 시스템 전체에서 실행 중인 프로세스
  • -f : full format
  • | : pipe. 왼쪽 명령 output -> 오른쪽 명령 input으로 넘김
  • grep : 문자열 검색. 키워드가 포함된 줄만 출력

Note

지금까지 pipe의 의미를 잘못 알고 있었다 sorry

AWS RDS Multi-AZ PIOPS config and metrics

Context

회사 AWS infra 비용 점검하다가 RDS 비용 분석

What I Learned

AWS RDS 설정을 이해하는데 필요한 개념들과 성능 체크를 위해 확인할 메트릭들

  • IOPS (Input/Output Operations Per Second) = 초당 디스크 작업 횟수 (디스크 성능)
  • Provisioning = 미리 예약해서 일정한 성능을 보장받는 것 (premium)
  • AZ (Availability Zone) = data center
  • Multi-AZ = primary DB & standby DB for 장애 대응
  • metrics to check in cloudwatch: readIOPS, writeIOPS, readLatency, writeLatency, diskQueueDepth, r/w throughput

Note

infra optimization is quite fun. why are we using piops anyway?

GitHub Actions SQL Review의 실제 경고 내용 확인하는 법

Context

PR에 붙은 SQL review bot 코멘트에 통계 요약만 보이고 실제 Advice 내용이 안 보였음.

What I Learned

PR 코멘트에는 summary만 올라오고, 실제 rule violation 상세 내용은 다른 곳에 있음.

확인 경로: PR → Checks 탭 → SQL Review → Details

Note

error check가 매우 strict해서 실제론 큰 문제가 아닌 경우도 많음.

운모 성모 성조

Context

일단해 중국어왕초보 1강

What I Learned

한어병음 -> 기본 운모, 성모, 성조

  • 운모: 모음. 기본 6개 조합해서 총 36개. a i o e u ü
  • 성모: 첫소리 자음. 21개
  • 성조: 4성 + 경음. -> /> /> \>

Note

오랜만에 동영상 강의 들으면서 소리내어 말하니까 어렵고 재밌다. 소리내어 반복 연습해야지

#-never-said-this-was-a-tech-blog

What is DB Migration

Context

Found .sql files inside db/migrations directory of an unfamiliar repo.

What I Learned

DB Migration = “Database migrations are a way to manage incremental, reversible changes to a database schema.”

개념의미
Schema evolutionDB 구조가 시간에 따라 변화
Migration script변경을 정의한 SQL
Versioning변경 순서를 관리
Migration history어떤 migration이 적용됐는지 기록
Reproducibility다른 환경에서 동일하게 재현

Note

DB structure evolves as the service goes on. Management with DB migration is essential for safety, data integrity, collaboration, environment control, reproducing DB env, rollback, cicd, etc.

우리 서비스도 이제 ByteBase 를 도입했으니 이걸 제대로 활용하는 방법을 익혀봐야겠다

Setting Custom Domain for Vercel App

Context

Setting up my freshly made dev blog with a custom domain

What I Learned

To configure a custom domain for a vercel project, you need to set up the domain DNS record.

DNS = “phonebook of the internet” (translating human-readable domain names into machine-readable IP addresses)

DNS record = a set of instructions used to connect domain names with internet protocol (IP) addresses within DNS servers.

Note

DNS record is what contains the actual configuration of a specific domain