이전 게시글 DB 추가 정보
https://hoozy.tistory.com/entry/%EB%B0%B1%EC%97%94%EB%93%9C-DB-%EC%B6%94%EA%B0%80-%EC%A0%95%EB%B3%B4
API
- Application Programming Interface의 약자로, 여러 프로그램들과 데이터베이스, 그리고 기능들의 상호 통신 방법을 규정하고 도와주는 매개체이다.
- 액세스 권한이 있는 앱의 권한 규정과 '서비스 요청'에 따라 데이터나 서비스 기능을 제공하는 메신저 역할을 한다.
Rest API
- Rest의 원리를 따르는 API
설계 예시
- URL 은 동사보다는 명사를, 대문자보다는 소문자를 사용해야 한다.
Bad Example : http://aaa.com/Running/
Good Example : http://aaa.com/run/
- 마지막에 슬래시 (/)를 포함하지 않는다.
Bad Example : http://aaa.com/test/
Good Example : http://aaa.com/test
- 언더바 대신 하이폰을 사용한다.
Bad Example : http://aaa.com/test_blog
Good Example : http://aaa.com/test-blog
- 파일확장자는 URI에 포함하지 않는다.
Bad Example : http://aaa.com/photo.jpg
Good Example : http://aaa.com/photo
- 행위를 포함하지 않는다.
Bad Example : http://aaa.com/delete-post/1
Good Example : http://aaa.com/post/1
Rest란
- Representational State Transfer의 약자로, HTTP/1.1을 기반으로 자원을 이름으로 구분하여 해당 자원의 상태를 주고 받는 모든 것을 의미합니다.
- 즉, HTTP URL을 통해 자원을 명시하고 HTTP Method(POST, GET, PUT, DELETE, PATCH 등)를 통해 해당 자원(URL)에 대한 CRUD Operation을 적용하는 것을 의미한다.
- CRUD Operation이란
- Create(POST), Read(GET), Update(PUT, PATCH), Delete(DELETE) 4가지를 일컫는 말이다.
- CRUD Operation이란
- 구성 요소
- 자원 : HTTP URL
- 자원에 대한 행위(Verb) : HTTP Method
- 자원에 대한 행위의 내용 (Representations) : HTTP Message PayLoad
- 특징
- Server-Client (서버-클라이언트 구조)
- Stateless (무상태)
- Cacheable (캐시 처리 가능)
- Layered System (계층화)
- Uniform Interface (인터페이스 일관성)
- 장점
- HTTP 프로토콜의 인프라를 그대로 사용하므로 REST API 사용을 위한 별도의 인프라를 구출할 필요가 없다.
- HTTP 프로토콜의 표준을 최대한 활용하여 여러 추가적인 장점을 함께 가져갈 수 있게 해 준다.
- HTTP 표준 프로토콜에 따르는 모든 플랫폼에서 사용이 가능하다.
- Hypermedia API의 기본을 충실히 지키면서 범용성을 보장한다.
- REST API 메시지가 의도하는 바를 명확하게 나타내므로 의도하는 바를 쉽게 파악할 수 있다.
- 여러 가지 서비스 디자인에서 생길 수 있는 문제를 최소화한다.
- 서버와 클라이언트의 역할을 명확하게 분리한다.
- 단점
- 표준이 자체가 존재하지 않아 정의가 필요하다.
- HTTP Method 형태가 제한적이다.
- 브라우저를 통해 테스트할 일이 많은 서비스라면 쉽게 고칠 수 있는 URL보다 Header 정보의 값을 처리해야 하므로 전문성이 요구된다.
- 구형 브라우저에서 호환이 되지 않아 지원해주지 못하는 동작이 많다.(익스폴로어)
RESTful 이란
- REST의 원리를 따르는 시스템을 의미한다.
- 하지만 REST를 사용했어도 RESTful은 아닐 수 있다. REST API의 설계 규칙을 올바르게 지킨 시스템을 RESTful하다 말할 수 있으며, 모든 CRUD 기능을 POST로 처리하는 API 혹은 URL 규칙을 올바르게 지키지 않은 API는 REST API의 설계 규칙을 올바르게 지키기 못한 시스템은 REST API를 사용하였지만 RESTful 하지 못한 시스템이라고 할 수 있다.
Authentication (인증)
- 누가 서비스를 사용하는지를 확인하는 절차이다.
- 쉽게 생각하면 웹 사이트에 사용자 아이디와 비밀번호를 넣어서 사용자를 확인하는 과정이 인증이다.
- API도 마찬가지로 API를 호출하는 대상을 확인하는 절차가 필요하다.
Authorization (인가)
- 해당 리소스에 대해서 사용자가 그 리소스를 사용할 권한이 있는지 확인하는 권한 체크 과정이다.
- 일반 사용자가 접근 가능한 페이지, 관리자만 접근 가능한 페이지 등을 위해 권한을 확인할 때 사용된다.
인증 방식 종류
1. OAuth
- 인터넷 사용자들이 비밀번호를 제공하지 않고, 다른 웹사이트 상의 자신들의 정보에 대해 웹 사이트나 애플리케이션의 접근 권한을 부여할 수 있는 공통적인 수단으로서 사용도는 접근 위임을 위한 개방형 표준이다.
- 에를 들어, 지마켓 등에서 구글이나 네이버 로그인을 할 때 접근 위임을 받는 것이다.
- 구성 요소
- Resource Owner
- 웹 서비스를 이용하려는 유저, 자원(개인정보)를 소유하는 자, 사용자
- resource는 개인 정보라고 보면 된다.
- Client
- 자사 또는 개인이 만든 애플리케이션 서버
- 클라이언트 라는 이름은 client가 resource server에게 필요한 자원을 요청하고 응답하는 관계여서 그렇다.
- Authorization Server
- 권한을 부여(인증에 사용할 아이템을 제공하는)해주는 서버이다.
- 사용자는 이 서버로 ID, PW 를 넘겨 Authorization Code를 발급 받을 수 있다.
- Client는 이 서버로 Authorization Code을 넘겨 Token을 받급 받을 수 있다.
- Resource Server
- 사용자의 개인정보를 가지고있는 애플리케이션 (Google, Facebook, Kakao 등) 회사 서버
- Client는 Token을 이 서버로 넘겨 개인정보를 응답 받을 수 있다.
- Access Token
- 자원에 대한 접근 권한을 Resource Owner가 인가하였음을 나타내는 자격증명
- Refresh Token
- Client는 Authorization Server로 부터 access token(비교적 짧은 만료기간을 가짐) 과 refresh token(비교적 긴 만료기간을 가짐)을 함께 부여 받는다.
- access token은 보안상 만료기간이 짧기 때문에 얼마 지나지 않아 만료되면 사용자는 로그인을 다시 시도해야한다.
- 그러나 refresh token이 있다면 access token이 만료될 때 refresh token을 통해 access token을 재발급 받아 재 로그인 할 필요없게끔 한다.
- Resource Owner
- 예를 들어 Client(개인 서비스)는 Resource Owner(사용자)를 대신해 로그인하는데, 이 때 필요한 정보를 Resource Server(kakao, naver 등)에서 얻어 서로 비교해 유효성을 판단한다.
- 이를 위해서 client는 Resource Owner로 부터 동의(허용)을 요청하고, Resource Server로 부터 client 신원확인하는 과정을 거친다.
- Resource Owner(유저) 입장
- 자신의 정보를 대신 사용하기 때문에 client가 어떤 정보를 활용하는지, 어떤 기능을 사용하려는지 모른다.
- 나쁜 마음을 가지면 개인정보를 마구잡이로 악용할수 있을 수도 있기 때문이다.
- 그러므로 client는 Resource Owner의 동의를 구해야 한다.
- Resource Server(kakao) 입장
- 다른 사람의 일을 대신 해주는 사람이 정말 그 사람일지 궁금할 수 있다.
- 마찬가지로 Resource Owner의 일을 수행 해주는 client가 정말 그 client일까 하는 물음이 있다.
- 이런 의미에서 Resource Server는 Resource Owner의 브라우저를 통해 client를 구분하는 값(code)를 전달한다.
2. Basic Auth
- 기본 인증으로, HTTP가 액세스 제어와 인증을 위한 프레임워크 중 가장 일반적인 방식
- 서버는 사용자가 누구인지 식별 할 수 있어야 함으로 auth를 통하여 식별하여 접근 권한을 결정한다.
- API 호출 시 http Authorization 헤더에 user id와 password를 base64로 인코딩한 문자열을 추가하여 인증하는 형식
- base64
- 8bit 바이너리 데이터 (ex: 실행 파일, zip파일)을 문자 코드에 영향을 받지 않는 공통 ASCII 영역의 문자로 이루어진 스트링 형태로 바꾸는 인코딩 방식
- 문자열을 기본적인 64개의 문자들로 변환해버리는 것
- 초기 문자보다 데이터의 크기는 더 커지지만 공통적으로 사용 할 수 있기 때문에 많이 사용 됨
- base64
- 절차
- 클라이언트가 API를 요청.
- 서버는 사용자에게 401 Unauthorized 응답과 함께 WWW-Authenticate 응답 헤더로 권한 부여 방법에 대한 정보를 제공한다.
- 클라이언트는 Authorication 요청 헤더에 인코딩 된 비밀번호, 그 외 다른 인증 파라미터들을 인증 정보를 포함하여 다시 요청한다.
- 성공하면 정상적인 상태 코드를 반환한다. 추가적인 인증 알고리즘에 대한 정보는 Authenitication-Info 헤더에 기술 한다.
- 장점 : 가장 크고 유일한 장점은 단순하고 편리함.
- 단점 : 보안 결함이 존재해서 보안이 중요한 정보의 경우에는 위험하다.
3. Token Auth
- 토큰 기반 인증 시스템은 클라이언트가 서버에 접속을 하면 서버에서 해당 클라이언트에게 인증되었다는 의미로 '토큰'을 부여한다. 이 토큰은 유일하며 토큰을 발급받은 클라이언트는 또 다시 서버에 요청을 보낼 때 요청 헤더에 토큰을 심어서 보낸다. 그러면 서버에서는 클라이언트로부터 받은 토큰을 서버에서 제공한 토큰과의 일치 여부를 체크하여 인증 과정을 처리하게 된다.
- 세션 기반 인증은 서버가 파일이나 데이터베이스에 세션 정보를 가지고 있어야 하고 이를 조회하는 과정이 필요해서 많은 오버헤드가 발생한다. 하지만 토큰은 세션과 달리 서버가 아닌 클라이언트에 저장되기 때문에 메모리나 스토리지 등을 통해 세션을 괸리했던 서버의 부담을 덜 수 있다. 토큰 자체에 데이터가 들어있기 떄문에 클라이언트에서 받아 위조되었는지 판별만 하면 된다.
- 서버(세션) 기반 인증 시스템은 클라이언트로부터 요청을 받으면 클라이언트의 상태를 계속해서 유지해놓고 사용한다. 그래서 Stateful 하며 이는 사용자가 증가함에 따라 성능의 문제를 일으킬 수 있으며 확장성이 어렵다는 단점이 있다.
- 토큰 기반 인증 시스템은 상태를 유지하지 않으므로 Stateless 한 특징을 가지고 있다.
- 절차
- 사용자가 아이디와 비밀번호로 로그인을 한다.
- 서버 측에서 사용자(클라이언트)에게 유일한 토큰을 발급한다.
- 클라이언트는 서버 측에서 전달받은 토큰을 쿠키나 스토리지에 저장해 두고, 서버에 요청을 할 때마다 해당 토큰을 서HTTP 요청 헤더에 포함시켜 전달한다.
- 서버는 전달받은 토큰을 검증하고 요청에 응답한다. 토큰에는 요청한 사람의 정보가 담겨있기에 서버는 DB를 조회하지 않고 누가 요청하는지 알 수 있다.
- 단점
- 쿠키/세션과 다르게 토큰 자체의 데이터 길이가 길어, 인증 요청이 많아질수록 네트워크 부하가 심해질수 있다.
- Payload 자체는 암호화되지 않기 때문에 유저의 중요한 정보는 담을 수 없다.
- 토큰을 탈취당하면 대처하기 어렵다. (따라서 사용 기간 제한을 설정하는 식으로 극복한다)
4. JWT (JSON Web Token)
- 인증에 필요한 정보들을 암호화시킨 JSON 토큰이다. 또한 JWP 토큰(Access Token)을 HTTP 헤더에 실어 서버가 클라이언트를 식별하는 방식이다.
- JSON 데이터를 Base64 URL-safe Encode를 통해 인코딩하여 직렬화 한것이며, 토큰 내부에는 위변조 방지를 위해 개인키를 통한 전자서명도 들어가 있다. 따라서 사용자가 JWT 를 서버로 전송하면 서버는 서명을 검증하는 과정을 거치게 되며 검증이 완료되면 요청한 응답을 돌려준다.
- Base64 URL-safe Encode : 일반적인 Base64 Encode 에서 URL 에서 오류없이 사용하도록 '+', '/' 를 각각 '-', '_' 로 표현한 것이다.
JWP 구조
- JWP는
.
을 구분자로 나누어지는 세 가지 문자열의 조합이다. 헤더(Header).내용(Payload).서명(Signature)
형태
- Header
{
"alg": "HS256", // 서명 암호화 알고리즘 (HMAX, SHA256, RSA 등)
"typ": "JWT" // 토큰 유형
}
- Payload
{
"sub": "1234",
"name": "Kim",
"iat": 1231231
}
- 정해진 타입은 없지만 세 가지의 타입으로 나뉜다.
- Registed Claims : 미리 정의된 클레임
- iss(issuer; 발행자),
- exp(expireation time; 만료 시간),
- sub(subject; 제목),
- iat(issued At; 발행 시간),
- jti(JWI ID)
- Public Claims : 사용자가 정의할 수 있는 클레임 공개용 정보 전달을 위해 사용
- Private Claims : 해당하는 당사자들 간에 정보를 공유하기 위해 만들어진 사용자 지정 클레임. 외부에 공개되도 상관없지만 해당 유저를 특정할 수 있는 정보들을 담는다.
- Registed Claims : 미리 정의된 클레임
- Signature
HMACSHA256{
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
256 bit secret code // 유일한 서버의 key 값
}
- header와 payload는 단순히 인코딩된 값이기 때문에 제 3자가 복호화 및 조작할 수 있지만, signature는 서버 측에서 관리하는 비밀키가 유출되지 않는 이상 복호화 할 수 없다. 따라서 Signature는 토큰의 위변조 여부를 확인하는데 사용된다.
- 절차
- 사용자가 ID, PW를 입력하여 서버에 로그인 인증을 요청한다.
- 서버에서 클라이언트로부터 인증 요청을 받으면, Header, PayLoad, Signature를 정의한다. Hedaer, PayLoad, Signature를 각각 Base64로 한 번 더 암호화하여 JWT를 생성하고 이를 쿠키에 담아 클라이언트에게 발급한다.
- 클라이언트는 서버로부터 받은 JWT를 로컬 스토리지에 저장한다. (쿠키나 다른 곳에 저장할 수도 있음). API를 서버에 요청할때 Authorization header에 Access Token을 담아서 보낸다.
- 서버가 할 일은 클라이언트가 Header에 담아서 보낸 JWT가 내 서버에서 발행한 토큰인지 일치 여부를 확인하여 일치한다면 인증을 통과시켜주고 아니라면 통과시키지 않으면 된다. 인증이 통과되었으므로 페이로드에 들어있는 유저의 정보들을 select해서 클라이언트에 돌려준다.
- 클라이언트가 서버에 요청을 했는데, 만일 액세스 토큰의 시간이 만료되면 클라이언트는 리프래시 토큰을 이용해서
- 서버로부터 새로운 엑세스 토큰을 발급 받는다.
- JWP는 정보 보호가 아닌 위조 방지가 목적이어서, 만약 해커가 임의로 Header나 Payload를 수정하더라도 원래의 signature가 불일치하면 조작으로 알고 인증 요청을 거부한다.
- 장점
- Header와 Payload를 가지고 Signature를 생성하므로 데이터 위변조를 막을 수 있다.
- 인증 정보에 대한 별도의 저장소가 필요없다. -> 데이터베이스 조회를 안해도 된다.
- 예를 들어, payload에 유저 이름과 유저 등급을 같이 보내면 서버에서는 유저이름을 가지고 DB를 조회하지 않아도 원하는 정보를 얻을 수 있다.
- JWT는 토큰에 대한 기본 정보와 전달할 정보 및 토큰이 검증됬음을 증명하는 서명 등 필요한 모든 정보를 자체적으로 지니고 있다.
- 클라이언트 인증 정보를 저장하는 세션과 다르게, 서버는 무상태(StateLess)가 되어 서버 확장성이 우수해질 수 있다.
- 토큰 기반으로 다른 로그인 시스템에 접근 및 권한 공유가 가능하다. (쿠키와 차이)
- OAuth의 경우 Facebook, Google 등 소셜 계정을 이용하여 다른 웹서비스에서도 로그인을 할 수 있다.
- 모바일 어플리케이션 환경에서도 잘 동작한다. (모바일은 세션 사용 불가능)
- 단점
- Self-contained : 토큰 자체에 정보를 담고 있으므로 양날의 검이 될 수 있다.
- 토큰 길이 : 토큰의 Payload에 3종류의 클레임을 저장하기 때문에, 정보가 많아질수록 토큰의 길이가 늘어나 네트워크에 부하를 줄 수 있다.
- Payload 인코딩 : payload 자체는 암호화 된 것이 아니라 BASE64로 인코딩 된 것이기 때문에, 중간에 Payload를 탈취하여 디코딩하면 데이터를 볼 수 있으므로, payload에 중요 데이터를 넣지 않아야 한다.
- Store Token : stateless 특징을 가지기 때문에, 토큰은 클라이언트 측에서 관리하고 저장한다. 때문에 토큰 자체를 탈취당하면 대처하기가 어렵게 된다.
- Access Token
- 클라이언트가 갖고 있는 실제로 유저의 정보가 담긴 토큰으로, 클라이언트에서 요청이 오면 서버에서 해당 토큰에 있는 정보를 활용하여 사용자 정보에 맞게 응답을 진행.
- 접근에 관여하는 역할이다.
- Refresh Token
- 새로운 Access Token을 발급해주기 위해 사용하는 토큰으로 짧은 수명을 가지는 Access Token에게 새로운 토큰을 발급해주기 위해 사용. 해당 토큰은 보통 데이터베이스에 유저 정보와 같이 기록.
- 재발급에 관여하는 역할이다.
gRPC
IPC 란 (Inter Process Communication)
- 프로세스간 통신하는 방법론.
- 아래에 자세하게 나와있습니다.
https://hoozy.tistory.com/entry/%EB%B0%B1%EC%97%94%EB%93%9C-%EC%9A%B4%EC%98%81-%EC%B2%B4%EC%A0%9C-2
RPC 란 (Remote Procedure Call)
- IPC의 소켓의 한계에서 등장한 기술로, 네트워크로 연결된 서버 상의 프로시저(함수, 메소드 등)를 원격으로 호출할 수 있는 기능이다. 네트워크 통신을 위한 작업 하나하나 챙기기 귀찮으니 통신이나 call 방식에 신경쓰지 않고, 원격지의 자원을 내 것처럼 사용할 수 있다.
- IDL(Interface Definication Language) 기반으로 다양한 언어를 가진 환경에서도 쉽게 확장이 가능하며, 인터페이스 협업에도 용이하다는 장점이 있다.
- 핵심은 'Stub(스텁)이라는 것이다. 서버와 클라이언트는 서로 다른 주소 공간을 사용하므로, 함수 호출에 사용된 매개 변수를 꼭 변환해줘야 한다. 안그러면 메모리 매개 변수에 대한 포인터가 다른 데이터를 가리키게 되어서 그렇다. 이 변환을 담당하는게 스텁이다.
- Client Stub은 함수 호출에 사용된 파라미터의 변환(Marchalling) 및 함수 실행 후 서버에서 전달된 결과의 변환을, Server Stub은 클라이언트가 전달한 매개 변수의 역변환(Unmarchalling) 및 함수 실행 결과 반환을 담당하게 된다.
- 하지만 구현의 어려움과 지원 기능의 한계 등으로 제대로 활용되지 못하며, 데이터 통신을 WEB을 활용해보려는 시도로 REST가 탄생후 안쓰게 되었다.
gRPC 란
- 구글 사에서 개발한 오픈소스 RPC 프레임워크로, PB(Protocol Buffer) 기반 Serizlaizer에 HTTP/2를 결합하여 RPC 프레임워크를 만든것이다.
- REST와 가장 큰 차이점은 HTTP/2를 사용한다는 것과 프로토콜 버퍼로 데이터를 전달한다는 점이다. 그렇기에 Proto File만 배포하면 환경과 프로그램 언어에 구애 받지 않고 서로 간의 데이터 통신이 가능하다.
HTTP/2 란
- HTTP/1.1은 기본적으로 클라이언트의 요청이 올 때만 서버가 응답하는 구조로 매 요청마다 connection을 생성해야만 한다. cookie 등 많은 메타 정보들을 저장하느 무거운 header가 요청마다 중복 전달되어 비효율적이고 느린 속도를 보여주었다. 이에 http/2에서는 한 connection으로 동시에 여러 개 메시지를 주고 받으며, header를 압축하여 중복 제거 후 전달하기에 version1에 비해 훨씬 효율적이다. 또한, 필요 시 클라이언트 요청 없이도 서버가 리소스를 전달할 수도 있기 때문에 클라이언트 요청을 최소화 할 수 있다.
ProtoBuf (Protocol Buffer, 프로토콜 버퍼)
- 구글 사에서 개발한 구조화된 데이터를 직렬화하는 기법이다.
- 직렬화 : 데이터 표현을 바이트 단위로 변환하는 작업을 의미한다. text 기반인 json 보다 직렬화된 protocol buffer는 필드 번호, 필드 유형 등을 1 byte로 받아서 식별하고, 주어진 length 만큼만 읽도록 하여 훨씬 덜한 용량인 byte를 차지한다.
Proto File
- Protocol Buffer 의 기본 정보를 명세하는 파일.
1) Message and Field
- Proto File 에서는 주고 받는 data들을 message 라는 것으로 정의한다. 이 메시지는 여러 가지 타입의 필드로 구성된다. 아래 예시로 query, page_number, result_per_page 라는 필드를 가지는 SearchRequest 라는 메시지를 정의해보겠습니다.
syntax = "proto3";
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
}
- Naming
- message 이름은 CamelCase 형태, field 이름은 under_bar 형태로 사용할 것을 권장한다. (필수는 아님) 유의할 것은 field 이름은 숫자로 시작할 수 없다는 점이다. 숫자를 표기해야 할 경우 꼭 문자 뒤에 표기해주어야 한다. ex) query_1
- CamelCase 형태 : 클래스 이름 작성법과 같다. ex) UserController 단어마다 첫 글자 대문자
- message 이름은 CamelCase 형태, field 이름은 under_bar 형태로 사용할 것을 권장한다. (필수는 아님) 유의할 것은 field 이름은 숫자로 시작할 수 없다는 점이다. 숫자를 표기해야 할 경우 꼭 문자 뒤에 표기해주어야 한다. ex) query_1
- Field Tag (=Field number)
- 메시지에 정의된 필드들은 각각 고유한 번호를 가지게 되고, 이는 인코딩 이후 binary data에서 필드를 식별하는데 사용된다. Field Tag는 최소1, 최대 536,870,911 로 지정 가능하며, 19000~19999는 프로토콜 버퍼 구현을 위해 reserved 된 값이므로 사용할 수 없다.
- 필드 번호가 1
15 일 때는 1 byte, 162047 은 2byte를 Tag로 가져가서 자주 호출되는 필드에 대해선 1~15로 지정해두는 것이 좋다.
- proto2 vs proto3
- 위 예제는 syntax = "proto3" 를 지정해줘서 proto version 3의 규약을 따르겠다고 선언했다. 이를 명시하지 않으면 default로 proto2 문법을 따르게 된다.
- proto2 지원 언어 : C++, JAVA, Python, Go, proto3 지원 언어 : C++, JAVA, Python, Go, Ruby, Objectice-C, C#, JavaScript, PHP, Dart
- Proto File Field Rule
- required : 필수로 가져야 할 필드 (proto2 만 쓴다.)
- optional : 해당 필드를 가지지 않거나 하나만 가짐 (proto2 만 쓴다.)
- repeated : 임의 반복 가능한 필드 (번호 및 값의 순서는 보존)
[packed=true]
옵션 : key-value 쌍 형태에서 value만 반복
message SearchRequestProto2 { // proto2 예제 -> required, optional을 필드 별로 꼭 명시해줘야한다.
required string query = 1;
optional int32 page_number = 2;
optional int32 result_per_page = 3;
}
message SearchRequestProto3 {
string query = 1;
int32 page_number = 2;
repeated int32 result_per_page = 3 [packed=true];
}
- proto3 버전에서 repeated 는 field를 배열의 형태로도 사용할 수 있게 된다. 필드는 key-value 구조로 저장되어, repeated field 를 사용할 때에도 key가 계속 붙게되는데 repeated 뒤에 packed 옵션을 주면 value만 반복하게끔 할 수 있다. -> 필드 번호는 바뀌지 않으니 되도록 이 옵션을 주면 보다 효율적이게 된다.
2) Package
- message type 이름을 중첩 없이 구분할 때 사용한다. 메시지 사용 시 package 를 명시함으로서 필드와 명확히 구분해준다. 아래 예제에서는 Open이라는 message를 타입으로 field 이름을 open으로 주어 모호한 정의를 package 로 구분하였다. 사실 foo.bar 라는 package를 굳이 쓰지 않아도 사용 불가한건 아니지만, 구성 메시지가 많다면 명확하게 구분될 수 있게 명시해 주는 것이 좋다.
// package 미사용 예제
message Open {
}
message Foo {
Open Open = 1;
}
// package 사용 예제
package foo.bar;
message Open {
}
message Foo {
foo.bar.Open open = 1;
}
3) Service
- RPC를 통해 서버 클라이언트에게 제공할 함수의 형태를 정의한다. 서비스 명과 RPC 메소드명 모두 CamelCase 형태를 권장한다. 옵션을 주지 않으면 단일 요청/응답으로 동작하지만, stream 옵션을 주면 RPC를 구현할 수 있다.
// Unary RPC
service SearchService {
rpc Search (SearchRequest) returns (SearchResponse);
}
// 양방향 Streaming RPC
service SearchService {
rpc Search (stream SearchRequest) returns (stream SearchResponse);
}
GraphQL
- 기존 API 호출 방식의 한계
- 우리는 클라이언트 개발을 하면서 서버와 통신을 해야할 때 보토 서버가 구현해놓은 API를 호출해 데이터를 보내거나 받아온다. 보통 하나의 View를 그리기 위해서는 여러 번 API를 호출해야 하고, 호출을 통해 받아온 데이터를 조합해 사용해야 한다.
- 이를 편하게 하기 위해 데이터 흐름을 만들고 해당 흐름에 순차 처리 로직을 위한 로직을 넣는 방식의 프로그래밍이 많이 사용되었다. 하지만 flatmap이나 map 등으로 데이터를 변환시키는 것은 결국 우리가 하나의 페이지를 그리기 위한 데이터를 조합하는 것은 클라이언트에서 처리해주어야 하기 때문에 클라이언트 로직이 복잡해지는 문제가 있다. 로직이 복잡해지면 유지보수가 어려워진다.
- 기존의 REST API나 다른 API 호출 방식은 이 문제를 해결하지 못한다 .기존 API들에서는 사용자는 서버에서 정의한 데이터 구조만을 한 번에 하나씩 가져올 수 있기 때문이다. 이를 해결하기 위해서는 앱 단에서 직접 뷰에 보여질 쿼리를 만들어 모든 데이터를 한 번에 가져와야 하기 때문이다.
- 이를 GraphQL은 클라이언트에서 자기에 필요한 데이터만을 쿼리할 수 있도록 하여 문제를 매우 직관적이고 깔끔하게 해결한다.
- 이를 위해 GraphQL은 클라이언트에서 사용할 Query Language를 정의한다. 클라이언트가 자신에게 필요한 데이터에 대한 Query를 선언해 GraphQL에 넘기면 GraphQL은 Query를 해석해 서버에서 필요한 데이터를 가져온 후 클라이언트에 해당 쿼리에 대한 데이터를 반환한다.
// id에 따라 저자의 이름과 이메일을 가지고 오고 싶을 때의 쿼리
query Author($id : Int) { // 타입
author(id: $id) {
name
email
// 추가적인 정보를 원하면 여기에 추가하면 된다.
}
}
- 즉, GraphQL은 다른 Query Language인 SQL등과 같이 쿼리를 선언하면 해당 값에 맞는 데이터를 가져온다. 이는 최근 선언형 패러다임이 절차적으로 쿼리를 처리하는 것이 아닌 선언적으로 쿼리를 처리함으로서 더욱 직관적으로 어떤 데이터가 오는지를 알 수 있다.
- GraphQL의 구조와 한계
- 클라이언트에서 쿼리를 선언하면 데이터를 절차적으로 처리해야 하는데 원래 SQL은 RDB(관계형 DB)가 해당 역학을 수행하는데 GraphQL은 서버와 클라이언트 사이에 GraphQL 이라는 Service Broker 레이어가 더 들어가고 이 GraphQL에서 절차적으로 처리를 수행한다. 즉, GraphQL이 중간에 레이어로 하나 더 있다고 봐야한다. 이는 클라이언트는 서버를 알 필요 없이 GraphQL 레이어랑만 통신함으로서 데이터를 가져오는 것이다. -> 기존에 클라이언트에서 데이터를 조합하던 역할이 GraphQL로 옮겨지는 것이다.
- 즉 이는 역할의 분리이다. 하지만 모듈로 역할을 분리하면 분리할수록 개별 모듈의 유지보수는 쉬워지지만, 구현은 복잡해진다. 마찬가지로 GraphQL을 만들면 클라이언트에서는 통신 로직의 구현이 편해지지만, 그만큼 서버에서는 일이 가중된다. 따라서 앱의 규모에 따라 GraphQL을 도입할 지 결정하는 것이 옳다.
- 그래도 GraphQL을 사용하면 유지 보수는 물론 확장이 용이한 API를 구현할 수 있으며, 이로 인해 이중 삼중으로 작업해야 하는 부분이 줄어든다. 또한 서버에서 할 수 있는 일을 스키마 단위로 파악할 수 있고, 필요한 정보를 유연하게 가져올 수 있다. 여러 번 요청하는 것을 한 번으로 줄여서 서버의 리소스 또한 최적화 할 수 있고, 클라이언트의 요청에 대한 부담이 줄어들게 된다.
다음 게시글 웹 보안
https://hoozy.tistory.com/entry/%EB%B0%B1%EC%97%94%EB%93%9C-%EC%9B%B9-%EB%B3%B4%EC%95%88
참고 자료
https://dongwooklee96.github.io/post/2021/03/28/rest-api-%EB%B3%B4%EC%95%88-%EB%B0%8F-%EC%9D%B8%EC%A6%9D-%EB%B0%A9%EC%8B%9D.html
https://inpa.tistory.com/entry/WEB-%F0%9F%93%9A-OAuth-20-%EA%B0%9C%EB%85%90-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC
https://velog.io/@minchoi/Basic-Auth%EB%9E%80-Node.js%EB%A1%9C-basic-Auth-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0
https://inpa.tistory.com/entry/WEB-%F0%9F%93%9A-JWTjson-web-token-%EB%9E%80-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC
https://khj93.tistory.com/entry/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-REST-API%EB%9E%80-REST-RESTful%EC%9D%B4%EB%9E%80
https://medium.com/naver-cloud-platform/nbp-%EA%B8%B0%EC%88%A0-%EA%B2%BD%ED%97%98-%EC%8B%9C%EB%8C%80%EC%9D%98-%ED%9D%90%EB%A6%84-grpc-%EA%B9%8A%EA%B2%8C-%ED%8C%8C%EA%B3%A0%EB%93%A4%EA%B8%B0-1-39e97cb3460
https://kotlinworld.com/330
댓글