"userspace를 절대 깨지 말라"는 조언이 유명하지만, 사실 그 반대 측면도 있다는 점을 잘 언급함. 즉, "커널 API는 예고 없이 깨질 수 있다"는 것임. 중요한 건 "모든 API를 아무렇게나 깨지 말라"가 아니라, "안정성을 선언한 부분만 절대 깨지 말라"는 미묘한 균형임
리눅스 커널이 userspace를 깨지 않는다고 해도, GNU libc는 굉장히 자주 userspace 호환성을 깸. 그래서 결과적으로 리눅스 사용자 공간은 커널 개발자들이 아무리 노력해도 깨지는 일이 빈번함. 새 버전 libc에서 빌드된 프로그램과 라이브러리는 하위 libc에서는 제대로 실행이 안 되기도 해서, 결국 모든 구성 요소를 한 번에 업그레이드해야 하는 실정임. 약간 아이러니하게도, 윈도우는 이미 수십 년 전에 redistributable 방식으로 이 문제를 해결했음
리눅스에는 유명하게도 안정적인 공개 드라이버 API가 없다는 점이 있는데, 이게 바로 구글이 Fuschia OS를 개발한 동기라고 들음. 리눅스는 사용자 공간과 하드웨어 모두에 대해 각기 다른 방식으로 방향성을 가진 셈임
글쓴이가 버전 기반 API를 별로 좋아하지 않는 듯하지만, 나는 애초에 앱을 만들 때부터 버전 관리를 반드시 도입하라고 항상 추천함. 미래를 예측할 수 없으니 언젠가 외부 요인으로 인해 깨지는 변경이 당신에게도 일어날 수밖에 없음
실제로 글쓴이도 버전 관리를 추천했다고 생각함. 본문에는 "버전은 API를 책임 있게 변경하는 방법"이라고 했으니, 결국 버전 관리 자체를 장려하는 셈임. 다만, 새 버전으로의 전환은 최후의 수단으로 하라고 함
나는 굳이 엔드포인트에 "v1"을 붙이지 말라는 의견에 동의함. 실제 API가 성장하면서 벌어지는 일은, 먼저 기존 엔드포인트에 필드나 옵션을 추가해서 하위 호환을 지키려고 노력함. 그리고, 완전히 호환이 안 되는 작업이 필요해지면, 보통 엔드포인트 이름 자체를 새로 짓고 아예 새로운 엔드포인트(/v2가 아니라)를 만듦. 만약 전체 API를 바꿔야 하면, 기존 서비스를 폐기하고 이름부터 새로 지은 전혀 다른 서비스를 런칭하게 됨. 25년간 일하면서 "/v1"과 "/v2"가 나란히 쓰이는 서비스를 딱 한 번만 봤음
저자의 의도가 처음부터 /v1을 엔드포인트에 넣지 말라는 건 아니라고 생각함. 요점은 새 버전(/v2)이 생기지 않도록 최선을 다해야 한다는 것임. /v2가 생기면 버그 픽스마다 양쪽에 다 코드 수정을 해야 하고, 조건 분기가 지수적으로 늘어나서 코드베이스가 스파게티처럼 복잡해짐. 결국 다중 버전을 지원하게 된 애초의 /v1 설계가 미래 호환에 대한 배려가 부족한 셈임
버전 관리를 나중에 추가하는 것도 문제없다고 봄. 예를 들어 처음에는 /api/posts로 시작하고, 다음 버전은 /api/v2/posts로 추가하면 충분함
처음부터 버전을 박아 넣는 방식에 동의하지 않음. 그렇게 하면 정말로 다중 버전이 자주 쓰이게 되는데, 그게 오히려 더 좋지 않다고 생각함
이 글 아주 유익했음. 여기에 한 가지 조언을 추가하겠음. API 문서를 얼마나 어렵게 얻을 수 있느냐와 API 품질은 반비례함. 만약 계약서에 사인해야만 문서를 얻을 수 있는 상황이라면, 그 API의 품질이 형편없을 것으로 가정해도 무방함
idempotency key를 comment 테이블에 따로 저장하는 대신, Redis 같은 key/value 저장소에 넣으라고 했는데, 모든 실패 케이스에서 이 방식이 확실한 idempotency를 보장할 수 있을지 궁금함. 가령 서버가 SET key 1 NX 같은 조건부 쓰기를 하다가 이미 key가 있는 걸 발견하면, 댓글 생성을 그냥 건너뛰어야 하는데, 이 시점에 앞선 요청이 실제로 DB에는 반영되지 않았을 수도 있음. idempotency key 저장은 실제 작업과 트랜잭션 단위로 같이 커밋되어야 하고, 필요시 롤백도 되어야 함. 결국 idempotency key 본질은 ‘이 작업 혹은 요청의 고유 ID’가 되어야 함. 예를 들어 “댓글 생성”, “댓글 업데이트” 등 각각에 맞는 리소스별 식별자여야 한다는 것임
idempotency를 위한 별도 컴포넌트(예: redis 등)를 더하는 건 지양해야 함. 추상화가 깨지거나 이상하게 동작하는 문제 혹은 딜리버리 보장에 대한 이해 부족에서 오는 오류가 생길 수 있음. 대신에, 쓰기 작업에 라벨이나 메타데이터를 함께 저장하는 식으로, 사용자가 직접 진척 상황을 추적하고 기존 데이터와 함께 보관하는 방식이 훨씬 나음
커서 기반 페이지네이션의 장점은, 사용자 입장에서 페이지를 로드하고 ‘다음’ 버튼을 누르는 사이 새 아이템이 추가돼도, 기존에 봤던 항목을 또 보지 않아도 된다는 점임. 커서 방식은 이전 페이지의 마지막 객체 ID를 기록해두고 그 이후 아이템을 주니까, 무한 스크롤에 특히 유용함. 반면, 커서 기반은 “N번째 페이지로 점프” 기능을 만들기 어렵다는 단점이 있음
커서는 반드시 불투명하게(opaque) 만들어야 DB 크기 같은 것을 외부에 노출하지 않을 수 있음. 그리고 커서에 상태 정보(검색 파라미터, 캐시 상태, 라우팅 정보 등)를 인코딩해서 더 다양한 기능을 구현할 수도 있음
요즘 "API"라고 하면 대부분 웹앱에 요청 보내고, 파라미터랑 헤더 세팅해서 데이터 가져오는 걸 떠올리는데, 본래 API란 "Application Programming Interface" 즉, ‘애플리케이션 프로그램의 인터페이스’라는 뜻임. 1940년대에 처음 쓰였고, 그 뒤 1990년대까지는 거의 다른 의미 없이 사용됐음. API의 역사는 80년이 넘으며, 엄청난 옛날 자료들도 많음. 그때 프로그래머들이 어떤 문제를 다뤄서 어떻게 풀었는지 고민해보면, 오늘날 본인에게도 도움이 되는 부분이 있을 거라고 봄
내부 사용자를 단순히 '사용자'로만 본다는 의견에는 동의하지 않음. 비록 다들 더 기술적인 사람들이고, 프로그래머일 확률이 높아도, 이들도 바쁘고 자기 프로젝트에 집중하느라 API 변경에 대응할 시간이나 여유가 부족한 경우가 많음. 가능하면, 오픈하기 전에 팀 내부에서 "dogfooding"(실사용) 테스트를 충분히 하는 게 중요함. 일단 외부에 공개되면, ‘userspace를 깨지 않는다’는 약속을 반드시 지켜야 함
내부 사용자라면 직접 컨택해서 마이그레이션을 유도할 수 있는 계측 도구들이 보통 구현돼 있음. 덕분에 API 버전 폐기도 가능해서, 버전 관리를 전략적으로 도입하는 게 충분히 매력적임. 실제 API 버전 관리에 참여해봤고, 기본적으로 이걸 안 쓰는 조직과 비교해서도 확실히 효과를 봤음
버전 관리 방식은 이 문제를 푸는 데 도움이 된다고 봄. 내부 사용자를 배려하는 최선의 방법 중 하나는 스펙에 대해 협업하고, 그 스펙의 작업 중인 버전도 이해관계자들에게 공유하는 것임. 계속 업데이트되는 문서라 해도, 기준점을 잡아주면 내외부 피드백도 원활해지고, 굳이 정책적인 충돌 위험만 피하면 매우 유용하게 쓸 수 있음
idempotency key를 redis에 저장하는 대신, 가능하다면 실제 데이터를 기록하는 동일 트랜잭션 내에서 idempotency key도 함께 저장하는 게 더 확실하다고 생각함
"userspace를 절대 깨지 말라"는 경고는 정말 중요함. Spotify, Reddit, Twitter 등 최근 이 원칙을 무시해서 아쉬웠음
Hacker News 의견
"userspace를 절대 깨지 말라"는 조언이 유명하지만, 사실 그 반대 측면도 있다는 점을 잘 언급함. 즉, "커널 API는 예고 없이 깨질 수 있다"는 것임. 중요한 건 "모든 API를 아무렇게나 깨지 말라"가 아니라, "안정성을 선언한 부분만 절대 깨지 말라"는 미묘한 균형임
리눅스 커널이 userspace를 깨지 않는다고 해도, GNU libc는 굉장히 자주 userspace 호환성을 깸. 그래서 결과적으로 리눅스 사용자 공간은 커널 개발자들이 아무리 노력해도 깨지는 일이 빈번함. 새 버전 libc에서 빌드된 프로그램과 라이브러리는 하위 libc에서는 제대로 실행이 안 되기도 해서, 결국 모든 구성 요소를 한 번에 업그레이드해야 하는 실정임. 약간 아이러니하게도, 윈도우는 이미 수십 년 전에 redistributable 방식으로 이 문제를 해결했음
리눅스에는 유명하게도 안정적인 공개 드라이버 API가 없다는 점이 있는데, 이게 바로 구글이 Fuschia OS를 개발한 동기라고 들음. 리눅스는 사용자 공간과 하드웨어 모두에 대해 각기 다른 방식으로 방향성을 가진 셈임
글쓴이가 버전 기반 API를 별로 좋아하지 않는 듯하지만, 나는 애초에 앱을 만들 때부터 버전 관리를 반드시 도입하라고 항상 추천함. 미래를 예측할 수 없으니 언젠가 외부 요인으로 인해 깨지는 변경이 당신에게도 일어날 수밖에 없음
실제로 글쓴이도 버전 관리를 추천했다고 생각함. 본문에는 "버전은 API를 책임 있게 변경하는 방법"이라고 했으니, 결국 버전 관리 자체를 장려하는 셈임. 다만, 새 버전으로의 전환은 최후의 수단으로 하라고 함
나는 굳이 엔드포인트에 "v1"을 붙이지 말라는 의견에 동의함. 실제 API가 성장하면서 벌어지는 일은, 먼저 기존 엔드포인트에 필드나 옵션을 추가해서 하위 호환을 지키려고 노력함. 그리고, 완전히 호환이 안 되는 작업이 필요해지면, 보통 엔드포인트 이름 자체를 새로 짓고 아예 새로운 엔드포인트(/v2가 아니라)를 만듦. 만약 전체 API를 바꿔야 하면, 기존 서비스를 폐기하고 이름부터 새로 지은 전혀 다른 서비스를 런칭하게 됨. 25년간 일하면서 "/v1"과 "/v2"가 나란히 쓰이는 서비스를 딱 한 번만 봤음
저자의 의도가 처음부터 /v1을 엔드포인트에 넣지 말라는 건 아니라고 생각함. 요점은 새 버전(/v2)이 생기지 않도록 최선을 다해야 한다는 것임. /v2가 생기면 버그 픽스마다 양쪽에 다 코드 수정을 해야 하고, 조건 분기가 지수적으로 늘어나서 코드베이스가 스파게티처럼 복잡해짐. 결국 다중 버전을 지원하게 된 애초의 /v1 설계가 미래 호환에 대한 배려가 부족한 셈임
버전 관리를 나중에 추가하는 것도 문제없다고 봄. 예를 들어 처음에는 /api/posts로 시작하고, 다음 버전은 /api/v2/posts로 추가하면 충분함
처음부터 버전을 박아 넣는 방식에 동의하지 않음. 그렇게 하면 정말로 다중 버전이 자주 쓰이게 되는데, 그게 오히려 더 좋지 않다고 생각함
이 글 아주 유익했음. 여기에 한 가지 조언을 추가하겠음. API 문서를 얼마나 어렵게 얻을 수 있느냐와 API 품질은 반비례함. 만약 계약서에 사인해야만 문서를 얻을 수 있는 상황이라면, 그 API의 품질이 형편없을 것으로 가정해도 무방함
idempotency key를 comment 테이블에 따로 저장하는 대신, Redis 같은 key/value 저장소에 넣으라고 했는데, 모든 실패 케이스에서 이 방식이 확실한 idempotency를 보장할 수 있을지 궁금함. 가령 서버가 SET key 1 NX 같은 조건부 쓰기를 하다가 이미 key가 있는 걸 발견하면, 댓글 생성을 그냥 건너뛰어야 하는데, 이 시점에 앞선 요청이 실제로 DB에는 반영되지 않았을 수도 있음. idempotency key 저장은 실제 작업과 트랜잭션 단위로 같이 커밋되어야 하고, 필요시 롤백도 되어야 함. 결국 idempotency key 본질은 ‘이 작업 혹은 요청의 고유 ID’가 되어야 함. 예를 들어 “댓글 생성”, “댓글 업데이트” 등 각각에 맞는 리소스별 식별자여야 한다는 것임
커서 기반 페이지네이션의 장점은, 사용자 입장에서 페이지를 로드하고 ‘다음’ 버튼을 누르는 사이 새 아이템이 추가돼도, 기존에 봤던 항목을 또 보지 않아도 된다는 점임. 커서 방식은 이전 페이지의 마지막 객체 ID를 기록해두고 그 이후 아이템을 주니까, 무한 스크롤에 특히 유용함. 반면, 커서 기반은 “N번째 페이지로 점프” 기능을 만들기 어렵다는 단점이 있음
요즘 "API"라고 하면 대부분 웹앱에 요청 보내고, 파라미터랑 헤더 세팅해서 데이터 가져오는 걸 떠올리는데, 본래 API란 "Application Programming Interface" 즉, ‘애플리케이션 프로그램의 인터페이스’라는 뜻임. 1940년대에 처음 쓰였고, 그 뒤 1990년대까지는 거의 다른 의미 없이 사용됐음. API의 역사는 80년이 넘으며, 엄청난 옛날 자료들도 많음. 그때 프로그래머들이 어떤 문제를 다뤄서 어떻게 풀었는지 고민해보면, 오늘날 본인에게도 도움이 되는 부분이 있을 거라고 봄
내부 사용자를 단순히 '사용자'로만 본다는 의견에는 동의하지 않음. 비록 다들 더 기술적인 사람들이고, 프로그래머일 확률이 높아도, 이들도 바쁘고 자기 프로젝트에 집중하느라 API 변경에 대응할 시간이나 여유가 부족한 경우가 많음. 가능하면, 오픈하기 전에 팀 내부에서 "dogfooding"(실사용) 테스트를 충분히 하는 게 중요함. 일단 외부에 공개되면, ‘userspace를 깨지 않는다’는 약속을 반드시 지켜야 함
내부 사용자라면 직접 컨택해서 마이그레이션을 유도할 수 있는 계측 도구들이 보통 구현돼 있음. 덕분에 API 버전 폐기도 가능해서, 버전 관리를 전략적으로 도입하는 게 충분히 매력적임. 실제 API 버전 관리에 참여해봤고, 기본적으로 이걸 안 쓰는 조직과 비교해서도 확실히 효과를 봤음
버전 관리 방식은 이 문제를 푸는 데 도움이 된다고 봄. 내부 사용자를 배려하는 최선의 방법 중 하나는 스펙에 대해 협업하고, 그 스펙의 작업 중인 버전도 이해관계자들에게 공유하는 것임. 계속 업데이트되는 문서라 해도, 기준점을 잡아주면 내외부 피드백도 원활해지고, 굳이 정책적인 충돌 위험만 피하면 매우 유용하게 쓸 수 있음
idempotency key를 redis에 저장하는 대신, 가능하다면 실제 데이터를 기록하는 동일 트랜잭션 내에서 idempotency key도 함께 저장하는 게 더 확실하다고 생각함
"userspace를 절대 깨지 말라"는 경고는 정말 중요함. Spotify, Reddit, Twitter 등 최근 이 원칙을 무시해서 아쉬웠음
참고로 https://jcs.org/2023/07/12/api 링크에 좋은 API 관련 권장사항이 잘 정리되어 있으니 함께 보길 추천함