logo
logo
프론트엔드
GraphQL
그래프QL은 페이스북이 2012년에 개발하여 2015년에 공개적으로 발표된 데이터 질의어이며, 필요한 데이터의 구조를 지정하여 요청 및 반환받을 수 있게 설계되어 있다.
StackOverflow 질문 수: 21181
Github Stars : ★ 20064
사용 기업
인공지능
교육
기타
모빌리티
소셜/컨텐츠
직장
패션
금융/보험
종합
이커머스
헬스케어
부동산/인테리어
여행
푸드테크
techstack-logo
슈퍼브에이아이
techstack-logo
클래스팅
techstack-logo
엔라이튼
techstack-logo
바로고
techstack-logo
당근
techstack-logo
모두싸인
techstack-logo
그린랩스
techstack-logo
버즈빌
techstack-logo
카카오스타일
techstack-logo
차이코퍼레이션
techstack-logo
42dot
techstack-logo
카카오엔터프라이즈
techstack-logo
카카오모빌리티
techstack-logo
카카오
techstack-logo
뱅크샐러드
techstack-logo
네이버
techstack-logo
네이버웹툰
techstack-logo
스포카
더 보기
기술 블로그 글
카카오스타일
내가 만든 non-null 변수에서 NullPointerException이 발생할 리가 없어!
안녕하세요, 카카오스타일 전시 UX팀의 샌즈입니다.모두들 알고 계시듯 null(널)은 값이 없음을 나타냅니다. 그리고 null 값인 객체에 접근하려고 하면 그 유명한 NullPointerException(NPE)이 발생합니다. 그런데 null 값을 가지지 않는 다고 설정한 변수에서 NPE가 발생하는 현상을 겪어 그 내용을 공유해보려고 합니다.다음은 Kotlin에서 non-null 변수를 선언한 예입니다.타입인 경우 null 값을 가질 수 있고, 으로 선언하면 null이 들어가면 안 된다는 뜻입니다. 카카오스타일에서 Kotlin 외에 사용 중인 TypeScript와 GraphQL에서도 비슷하게 설정가능합니다.non-null 변수에 null이 들어가는 현상지그재그 특가 진입시 ad_noti_status라는 필드가 ‘disagree’이면 알림 유도 UI를 제공하도록 되어 있었습니다. 이 필드 값을 반환하는 코드는 다음과 같습니다.일부 사용자가 알림 동의를 받지 않았음에도 UI가 노출되지 않아 분석을 해보니 위 코드에서 NPE가 발생하고 있었습니다.문제 재현을 위해 다음과 같이 코드를 작성해봤습니다.실행하면 다음과 같은 결과를 보여줍니다.이런 에러가 나는데는 몇가지 조건이 겹쳤습니다.잠깐! NPE는 null인 객체를 접근해야 에러가 나는 건데, user는 null이 아닌데? 맞습니다. 실제 환경에서도 user가 null이면 로그를 찍게 해봤는데 로그에 찍히는게 없었습니다.해당 서비스는 오래전에 만들어졌는데, 그 뒤로 Kotlin 버전 업그레이드를 하지 않은채 1.4.20을 사용하고 있었습니다. Kotlin을 1.5로 업그레이드 했더니 문제가 발생하지 않았습니다. (user가 null이면 당연히 NPE가 발생합니다.) when을 if로 바꿔도 문제가 발생하지 않았기 때문에 오래전 Kotlin 버전에서 when 동작에 특이한 부분이 있다고 짐작할 수 있습니다.다음은 비슷한 상황에 enum을 추가 적용해본 것입니다.Kotlin 최신 버전(2.0.0)에서 실행해본 결과입니다.when를 if의 문법적 설탕 정도로 생각했는데 동작이 조금 다른 것을 확인할 수 있습니다.Kotlin 언어에는 null과 non-null 구분이 있지만, 그 기반이 되는 JVM에서는 그런 구분이 없습니다. 그렇다보니 언어의 제약과 상관없이 내부 값이 null을 가질 가능성이 존재합니다. (TypeScript도 마찬가지기 때문에 JSON 파싱 후 값의 타입을 검증해주면 좋습니다.)위 문제를 사내에 공유했을 때 비슷한 문제가 있어서 Gson 대신 Jackson을 사용했다는 답글이 있었습니다.테스트 코드를 Jackson으로 재작성했을 때 파싱 과정에 에러가 나는 것을 확인했습니다.Kotlinx도 마찬가지로 파싱시 에러를 발생합니다.Kotlinx는 동작이 다른 부분이 하나 더 있습니다. ad_noti_status가 nullable, 즉 String?으로 선언되었을 때, Gson, Jackson은 문제가 없었지만, Kotlinx는 에러를 반환했습니다. Kotlinx는 JSON 문자열이 일
graphql
kotlin
카카오엔터테인먼트FE
마법소녀 이세계 아이돌 웹툰 런칭! BFF 장애 대응기
카카오페이지 웹 서비스는 graphql을 활용해 BFF를 운영하고 있습니다. 팀 내부에서는 graphql을 사용하는 방법에 대한 노하우들이 적립되어 DX(Developer Experience) 향상과 타 팀과의 커뮤니케이션에 많은 도움을 받으며 BFF를 사용하고 있었는데요. 유명 스트리머 우왁굳의 버추얼 걸그룹 이세계 아이돌의 웹툰이 카카오페이지에 런칭하는 날 트래픽이 3배 이상 몰리게 되며 BFF 서버의 CPU가 100%를 찍는 장애가 발생하게 됩니다. 저희가 문제를 겪으며 알아낸 해결책이 어떤 분께는 도움이 될 수 있을 것 같아 이 글을 쓰게 되었습니다. 문제를 찾아 나가며 알게 된 꿀팁들도 적어 볼 테니 재밌게 봐주셨으면 좋겠습니다.이세계 아이돌 웹툰 런칭 서비스 장애2023년 6월 21일 22시 이세계 아이돌의 웹툰 마법소녀 이세계 아이돌 이 카카오페이지와 카카오웹툰에 런칭하게 됩니다. 스트리머 우왁굳과 버추얼 아이돌 그룹 이세계 아이돌 멤버들이 트위치에서 실시간 생중계를 하며 런칭을 기다렸고, 많은 시청자들이 웹을 통해 작품에 접속하게 되면서 서버가 응답을 내려주지 못하는 장애가 일어나게 됩니다. 수분 정도 페이지가 뜨지 않는 현상이 있었고, 리소스 대비 생각보다 TPS가 안 나오고 있는 것을 발견하여 성능 개선을 우선 과제로 보고 여러 가설을 세워 문제를 찾아나가기 시작했습니다.BFF는 백엔드 API를 호출해서 응답을 가공해 클라이언트로 내려주는 역할 정도만 하고 있어서 CPU가 올라갈 만한 문제의 원인에 감이 안 오는 상황이었는데요. 저희 팀은 여러 가지 가설을 세워 문제의 원인을 찾아내고자 했습니다. 원인이 확실하게 떠오르지 않아 생각나는 대로 가설 리스트를 만들고 하나씩 넣어보며 원인을 찾아 나섰습니다. 가설을 말씀드리기 전에 일단 저희 구성도를 간단히 설명해 드리겠습니다.저희는 서비스 운영에 쿠버네티스를 활용하고 있고, 서버사이드 렌더링을 하는 next.js pod들과(인스턴스라 생각해 주시면 되겠습니다.) BFF를 담당하는 pod들이 따로 떠 있는 상황입니다. BFF에 요청이 들어오면 그에 맞는 백엔드 API를 호출하고, API 응답을 받은 후에는 클라이언트가 요청한 gql 포맷에 맞게 응답을 가공해 내려주는 작업을 진행합니다. BFF 환경은 node.js express에 apollo server를 사용해 띄우고 있었는데요. REST API로 데이터를 받아와 가공 정도만 하는 BFF 서버가 서버사이드 렌더링을 하여 html을 그려주는 next.js 서버보다 성능이 안 나오는게 이해가 안 되는 상황이었죠.제일 간단하게 생각을 해봤을 때 장애 당시 백엔드 응답이 늦어서 BFF도 응답을 내려주기까지 시간이 오래 걸려 타임아웃이 발생한 게 아닐까 하는 생각이 들었습니다. 하지만 당시 백엔드 팀의 모니터링 결과로는 응답 중에서는 특이하게 느린 응답이나 생각보다 부하가 없었다는 얘기를 전달받게 됩니다. 그렇다면 저희 쪽 BFF 서버에서 뭔가의 문제가 있는 것인데.. 정확한 확인을 위해 현 상황에서 nGrinder를 통해 부하 테
apollo
expressjs
graphql
kubernetes
nodejs
카카오스타일
Storybook의 MSW mock 구조 개선
카카오스타일의 파트너센터 서비스에서는 UI 테스트와 문서화를 위해서 Storybook과 MSW를 사용하고 있습니다. Storybook의 각 스토리마다 MSW GraphQL mock을 개별로 정의해서 사용하고 있었는데, 이로 인해 코드가 중복되고 mock 누락으로 인한 오류가 자주 발생했습니다. 이런 문제를 해결하기 위해, 스토리마다 따로 정의된 MSW mock을 한 군데로 모아서 관리할 수 있는 새로운 구조를 제안하고, recast를 사용한 변환 스크립트를 작성하여 마이그레이션 하는 과정까지 다뤄봤습니다.Storybook에서 MSW를 사용할 수 있도록 해주는 애드온인 msw-storybook-addon은 아래와 같은 구조를 예시로 들고 있으며, 카카오스타일 프로젝트에서도 동일한 방식으로 스토리를 작성하고 있습니다.이 방식의 장점은 다음과 같습니다.• 컴포넌트가 필요로 하는 API를 문서화할 수 있습니다.• 테스트할 때에도 정의된 API mock만 사용하므로, 의도치 않게 테스트 케이스가 변하지 않습니다.단점은 다음과 같습니다.• API가 업데이트될 때 관련된 mock을 찾아서 수정해야 합니다.• 컴포넌트를 수정할 때 API mock 추가를 누락해서 스토리에 오류가 발생하는 경우가 자주 있습니다.• 여러 컴포넌트에서 공통적으로 사용되는 컴포넌트에 연관된 API가 수정되는 경우에는, 영향받는 모든 스토리를 수정해야 합니다.파트너센터는 하나의 API 서버만 바라보고 있고, 페이지마다 사용할 수 있는 API도 모두 동일합니다. 그럼에도 불구하고 스토리마다 mock을 따로 작성하는 것이 관리의 어려움을 초래하고 있고, 장점보다 단점이 더 크다고 생각하게 되었습니다.그래서, 스토리북의 API mock을 공통화해서 관리하는 방법을 고민하게 되었습니다. 간단하게, 아래처럼 하나의 파일에 모든 mock을 모아둔다고 생각하면 됩니다.또한, API 응답을 컴포넌트의 props으로 직접 전달하는 사례도 존재하기 때문에, 이럴 때 사용할 수 있도록 API 응답을 그대로 상수로 정의해두는 fixtures도 사용해볼 수 있습니다.API mock을 모아놓고 관리하면 다음과 같은 장점이 있습니다.• 중복되는 API mock이 최소화되어, API가 변경될 때 관리가 쉽습니다.• 컴포넌트에 API 호출이 추가되었을 때에도, 스토리마다 따로 대응할 필요가 없기 때문에 번거로움을 줄일 수 있습니다.• 스토리를 작성할 때 API mock에 대해 별도로 고려하지 않아도 되어 작성 난이도가 크게 줄어듭니다.• 전반적으로 프로젝트 내부에서 유지보수나 코드 재사용이 원활해집니다.단점으로는 다음과 같은 것들이 있습니다.• 전역에 API가 존재한다는 가정이 들어가기 때문에, 프로젝트 외부에서 컴포넌트를 추출해서 사용하는 것이 어려워질 수 있습니다.• mock을 어떻게 관리할 것인지에 대한 가이드라인이 필요합니다.• API mock을 오버라이드해야 하는 상황이 발생할 때, 이를 어떻게 처리할 것인지에 대한 고민이 필요합니다.• 기존에 사용하던 모든 스토리를 수정해야 하는 문제가 있습니다.
graphql
nodejs
storybook
인프런
Code generator 개발기 (Part 1)
안녕하세요. 인프랩 프런트엔드 개발자 루카스입니다.아마 프런트엔드 개발을 하다 보면 code generator라는 단어를 들어보셨을 거로 생각합니다. Code generator는 서버에서 선언한 약속한 데이터 타입을 이용해 클라이언트에서 사용할 수 있는 코드를 자동으로 만들어 주는 프로그램을 의미합니다. 특히 프런트엔드 개발자의 입장에서 살펴보면 API의 변경 사항을 추적하고, type-safe 한 개발을 하고자 할 때 이런 자동화 도구는 상당한 이점을 제공합니다.인프랩도 본격적으로 레거시 시스템을 개선하면서부터 API의 타입을 다뤄야 할 필요성이 생기기 시작했습니다. 하나의 서버에서 동작하던 인프런이 API 서버와 클라이언트로 나누어지기 시작하면서 API를 통해 기능을 구현해야 하는 일이 많아졌기 때문입니다.이번 포스트에서는 code generator를 직접 개발하게 된 이유에 대해 말씀드리려고 합니다. 다음 포스트에서는 구현 방법과 도입 후 어떤 점이 바뀌었는지 공유하고자 합니다.1. RESTful API를 마주하다.제품을 만들다 보면 많은 고민을 마주하게 되지만, 그중에서도 API를 요청하는 부분을 관리하는 것은 언제나 깊은 고민이 수반됩니다.일단 어떤 라이브러리를 사용할 것인가부터, 코드를 어디에 어떻게 위치시켜야 API의 변화에 쉽고 유연하게 대응할 수 있을지 같은 정답이 없는 선택이 필요합니다. 덧붙여, 시간이 지날수록 늘어나는 API endpoint의 관리도 걱정거리 중 하나로 다가오곤 합니다.코드 베이스에서의 고민특히 이제는 업계 표준이라고 말할 수 있는 TypeScript를 사용하게 되면 한가지 고민을 추가로 해야 하는데, 바로 API의 반환 타입을 어떻게 관리할 것에 대한 고민입니다.TypeScript의 특성상 API의 반환 값을 지정하지 않고 사용할 수 없기 때문에 반드시 선언해 줘야 합니다. 이를 위해 프런트엔드 개발자는 보통 두 가지 방법 중 하나를 선택하게 되는데, 하나는 모든 요청에 대한 반환 타입을 any로 선언하는 것이고 다른 하나는 문서에 기술된 반환 값을 보고 타입을 수동으로 관리하는 것입니다.경험상 타입을 any로 관리하지 않고 직접 수동으로 관리하게 되면 문제가 연이어 발생하곤 했습니다.프로젝트 초기에는 타입을 꾸준히 업데이트프로젝트가 막바지에 이를수록 여러 가지 이유(바쁘거나, 급한 버그 수정 등)로 타입을 적당히 컴파일이 실패하지 않는 선에서 관리제품 배포 후 유지보수가 진행될수록 새로운 타입이 양산되거나 index signature, union, intersection 등이 남발되며 runtime 안정성은 점점 떨어짐결국 타입 선언이 하나의 레거시가 되어 아무도 신경을 쓰지 않게 됨물론 모든 프로젝트에서 발생하는 일은 아니지만 대체로 위와 같이 흘러갔을 경우, API와 클라이언트 간 인터페이스 역할을 맡은 타입의 의미가 유명무실하게 변합니다.이 뜻은 곧 API와 클라이언트 간 역할, 책임, 협력을 신뢰할 수 없게 된다는 의미로 귀결됩니다. 제품을 위해 가장 긴밀하게 협력해야 하는 두 객체가 서로
graphql
swagger
연관 기술 스택
techstack-logo
Apollo
techstack-logo
MobX
techstack-logo
Redux
techstack-logo
Relay
Copyright © 2024. Codenary All Rights Reserved.