나의 개발일지 TIL(Today I learned) - rest API, HTML, CSS, Javascript 특강
Today I learned
- rest API
- HTML, CSS, Javascript
- 동적 프로그래밍(Dynamic Programming)
rest API
API란?
API는 Application Programming Interface의 줄임말이고 응용 프로그램에서 데이터를 주고 받기 위한 방법을 말한다
인터페이스(Interface)는 어떤 두가지가 서로 정보를 교환하기 위한 수단이나 방법을 말한다
Server API는 데이터를 주고 받는 양식, 인증, 호출 제한
Open API는 누구나 사용할 수 있도록 공개된 API를 말한다 왜 사용할까?
빠르고 유연한 비즈니스를 구축할 수 있고 생태계를 확립한다 적은 비용과 빠른 시스템 도입이 가능하고 비즈니스 품질을 향상시킨다
REST란?
REpresentational State Transfer
자원을 이름으로 구분해 해당 자원의 상태를 주고 받는 모든 것을 의미한다
자원(resource)의 표현(representation)에 의한 상태 전달을 말한다
자원: 해당 소프트웨어가 관리하는 모든 것(문서, 그림, 데이터, 해당 소프트웨어 자체 등)
표현: 그 자원을 표현하기 위한 이름(데이터베이스의 학생 정보가 자원이면 student를 자원 표현으로 정한다)
상태전달: 데이터가 요청되는 시점에 자원의 상태를 전달한다(JSON 또는 XML을 통해 데이터를 주고 받는다)
REST는 기본적으로 웹의 기존 기술과 HTTP 프로토콜을 그대로 활용하기 때문에 웹의 장점을 최대한 활용할 수 있는 아키텍쳐 스타일이다 그리고 네트워크 상에서 Client와 Server 사이의 통신 방식 중 하나다
REST의 구성요소
자원(Resource) - URI
모든 자원에는 고유한 ID가 존재하고 이 자원은 Server에 존재한다
자원을 구별하는 ID는 "students/:student_id"와 같은 HTTP URI이다
Client는 URI를 이용해 자원을 지정하고 해당 자원의 상태에 대한 조작을 Server에 요청한다
행위(Verb) - Method
HTTP 프로토콜의 Method를 사용한다
HTTP 프로토콜은 GET, POST, PUT, PATCH, DELETE의 Method를 제공한다 (CRUD)
GET | Read : 정보 요청, URI가 가진 정보를 검색하기 위해 서버에 요청한다 |
POST | Create : 정보 입력, 클라이언트에서 서버로 전달하려는 정보를 보낸다 |
PUT | Update : 정보 업데이트, 주로 내용을 갱신하기 위해 사용한다. (데이터 전체를 바꿀 때) |
PATCH | Update : 정보 업데이트, 주로 내용을 갱신하기 위해 사용한다. (데이터 일부만 바꿀 때) |
DELETE | Delete : 정보 삭제 (안전성 문제로 대부분 서버에서 비활성화한다) |
표현(Representation of Resource)
Client와 Server가 데이터를 주고받는 형태로 JSON, XML, TEXT, RSS등이 있다
JSON, XML을 통해 데이터를 주고 받는 것이 일반적이다
REST의 특징
유니폼 인터페이스: HTTP 표준만 따르면 어떤 언어나 플랫폼에서 사용이 가능한 인터페이스 스타일이다
Stateless(상태 정보 유지x): REST는 상태 정보를 유지하지 않는다 서버는 각각의 요청을 완전히 다른 것으로 인식하고 처리한다
Cacheable(캐시가능): HTTP라는 기존 웹 표준을 그대로 사용하기 때문에 기존 인프라를 그대로 활용 가능하다 그래서 HTTP가 가진 캐싱 기능을 적용 가능하다
Self-descripriveness(자체 표현 구조): REST API 메세지만 보고도 쉽게 이해 할 수 있는 자체 표현 구조로 되어있다
REST의 장단점
장점
HTTP 프로토콜 인프라를 그대로 사용하기 때문에 REST API 사용을 위한 별도의 인프라를 구축할 필요가 없다
HTTP 프로토콜 표준을 최대한 활용하여 여러 추가적인 장점을 함께 가져갈 수 있게 해준다
Hypermedia API의 기본을 충실히 지키면서 범용성을 보장한다
REST API 메시지가 의도하는 바를 명확하게 나타내므로 의도하는 바를 쉽게 파악할 수 있다
여러가지 서비스 디자인에서 생길 수 있는 문제를 최소화한다
서버와 클라이언트의 역할을 명확하게 분리한다
단점
표준이 존재하지 않는다
HTTP Method 형태가 제한적이다
브라우저를 통해 테스트할 일이 많은 서비스라면 쉽게 고칠 수 있는 URL보다 Header값이 더 어렵게 느껴진다
구형 브라우저가 아직 제대로 지원해주지 못하는 부분이 존재한다
RESTful API란?
RESTful은 일반적으로 REST라는 아키텍처를 구현하는 웹 서비스를 나타내기 위해 사용되는 용어 REST API를 제공하는 웹 서비스를 RESTful하다고 할 수 있다
RESTful은 REST를 REST답게 쓰기 위한 방법으로 누군가가 공식적으로 발표한게 아니다
REST 원리를 따르는 시스템을 RESTful이란 용어로 지칭된다
RESTful API의 목적은 이해하기 쉽고 사용하기 쉬운 REST API를 만드는 것이다 RESTful한 API를 구현하는 목적이 성능향상이 아니라 일관적이 컨벤션을 통한 API의 이해도 및 호환성을 높이는 것이 주 동기다 성능이 중요한 상황에서는 굳이 RESTful한 API를 구현할 필요는 없다
RESTful API의 특징은 사내 시스템들도 REST 기반으로 시스템을 분한해 확장성과 재사용성을 높여 유지보수 및 운용을 편리하게 할 수 있다
RESTful API 설계 규칙
슬래시 구분자( / )는 계층 관계를 나타내는데 사용한다
"http://restapi.example.com/houses/apartments" // 이렇게 하자
URI마지막 문자로 슬래시 구분자( / )를 포함하지 않는다
URI에 포함되는 모든 글자는 리소스의 유일한 식별자로 사용되어야 하며 URI가 다르다는 것이 리소스가 다르다는 것이고 역으로 리소스가 다르면 URI도 달라져야 한다
REST API는 분명한 URI를 만들어 통신을 해야 하기 때문에 혼동을 주지 않도록 URI 경로의 마지막에는 슬래시 구분자를 사용하지 않는다
"http://restapi.example.com/houses/apartments/" // 이렇게 하지말자
URI경로는 소문자가 적합하다 대문자 사용을 피하자
RFC 3986(URI 문법 형식)은 URI 스키마와 호스트를 제외하고는 대소문자를 구별하도록 규정했다
파일확장자는 URI에 포함하지 않는다
REST API에서는 메시지 바디 내용의 포맷을 나타내기 위한 파일 확장자를 URI안에 포함시키지 않는다
"http://restapi.example.com/members/soccer/345/photo.jpg" //이렇게 하지말자
파일확장자는 Accept header를 사용하자
GET / members/soccer/345/photo HTTP/1.1 Host: restapi.example.com Accept: image/jpg
리소스 간에 연관 관계가 있는 경우 /리소스명/리소스ID/ 관계가 있는 다른 리소스명
GET : /users/{userid}/devices (일반적으로 소유 ‘has’의 관계를 표현할 때)
응답 상태 코드
1xx : 전송 프로토콜 수준의 정보 교환
2xx : 클라어인트 요청이 성공적으로 수행됨
3xx : 클라이언트는 요청을 완료하기 위해 추가적인 행동을 취해야 함
4xx : 클라이언트의 잘못된 요청
5xx : 서버쪽 오류로 인한 상태코드
RESTful 하지 못한 경우
CRUD 기능을 모두 POST로만 처리하는 API
route에 resource, id 외의 정보가 들어가는 경우 (/students/updateName)
REST API의 한계는 복잡한 서비스나 클라이언트의 요청사항에 따라 Over-Fetching과 Under-Fetching이 발생한다
또한 REST API로 여러 환경에서 필요한 정보들을 Resource 별로 Endpoint를 갖도록 구현하는 것은 어렵다 한마디로 비슷하지만 Endpoint가 다른 API가 많이 파생된다
Over-Fetching
사용자 데이터를 조회하는 /user/API가 있다고 생각해보자
이 때 사용자 번호 1에 해당하는 데이터를 조회한다면 아래와 같은 형태가 된다
GET /user/1/
response body
{
"user_no":1,
"user_name": "test",
"user_grade": "VVIP",
"zip": "11053",
"last_login_timetamp": "2019-08-08 12:11:44",
...
}
여기서 클라이언트는 1번에 해당하는 유저의 이름만을 사용하고자 한다고 해도 유저 이름만 반환하는 API가 없다면
/user/1/API를 호출하고 user_name을 가져와 사용해야한다 이때 user_grade, zip등등 데이터는 사용하지 않지만 같이 반환 된다 이는 리소스의 낭비라고 볼 수 있고 Over-Fetching이라고 명한다
- 필요한 정보보다 더 많은 데이터를 전달 받는다
- 불필요한 리소스 낭비가 발생한다
- 필요한 정보만 골라내는 추가 작업이 발생한다
Under-Fetching
쇼핑몰 서비스의 경우 로그인한 사용자의 장바구니 정보를 보여준다고 생각하자
/user/1/
/cart/
/notification/
/wish/
...
요청에 맞게 유효한 데이터를 보여주기 위해 여러 API를 호출하게 되는 경우를 Under-Fetching이라고 한다
- 필요한 데이터를 만들기 위해 여러 번의 호출이 필요하다
- 추가적인 리소스 요청이 발생한다
- 여러 요청을 통해 전달 받은 정보를 조합하는 추가 작업이 필요하다
REST API 방법론으로 API를 만든다고 하면 자원 별로 Endpoint를 갖기 때문에 비슷하지만 다른 API가 많다
/item/
/item/detail/
/item/image/
/item/notice/
/item/manage/
...
이는 서비스가 커져갈 때 관리 포인트가 늘어나게 되어서 개발자나 클라이언트에게 부담이 된다
GraphQL
위에서 설명한 REST API의 한계를 극복하고자 만들어졌다
Endpoint는 통상 1개만 생성하고 클라이언트에게 필요한 데이터는 클라이언트가 직접 쿼리를 작성, 호출하여 반환 받는다
정보를 요청하는 쪽에서 원하는 형태로 정보를 가져오고 수정할 수 있는 Query Language 이다
Over-Fetching 해결
요청 쿼리
query{
user(user_no:1){
user_name
}
}
반환 데이터
{
"data": {
"user": {
"user_name": "jim",
}
}
}
Under-Fetching 해결
요청 쿼리
query{
cart{
product_name
price
}
notification{
is_read
}
user(user_id:1){
user_name
user_grade
}
}
반환 데이터
{
"cart": [{
"product_name": "shoes",
"price": 12000
}, ...],
"notifications": [{
is_read: true
}],
"user": {
"user_name": "jim",
"user_grade": "VVIP"
}
}
장점
클라이언트가 필요한 데이터만 반환 가능하다
한 번의 호출로 원하는 데이터를 한번에 가져올 수 있다
확장이 용이하다
단점
백엔드, 클라이언트 개발자 양쪽 다 러닝커브가 있다
단순한 서비스에서는 사용하기 복잡하다
캐싱 기능의 구현이 복잡하다
요청이 text로 날아가기 때문에 File 전송 등을 구현하기 어렵다
HTTP와 GQL기술 스택 비교
동적 프로그래밍(Dynamic Programming)
동적 프로그래밍(Dynamic Programming이란 복잡한 문제를 간단한 여러 개의 문제로 나누어 푸는 방법을 말한다
이것은 부분 문제 반복과 최적 부분 구조를 가지고 있는 알고리즘을 일반적인 방법에 비해 더욱 적은 시간 내에 풀 때 사용한다 여러 개의 하위 문제를 풀고 그 결과를 기록하고 이용해 문제를 해결하는 알고리즘이다
문제를 반복해서 해결해 나가는 모습이 재귀 알고리즘과 닮았지만 동적 프로그래밍은 결과를 기록하고 이용한다
용어정리를 간단하게 하자
결과를 기록하는 것 메모이제이션(Memoization), 겹치는 부분 문제(Overlapping Subproblem)라고 한다
피보치수열을 동적 프로그래밍으로 만들어보자
input = 50
memo = {
1: 1,
2: 1
}
def fibo_dynamic_programming(n, fibo_memo):
if n in fibo_memo:
return fibo_memo[n]
nth_fibo = fibo_dynamic_programming(n - 1, fibo_memo) + fibo_dynamic_programming(n - 2, fibo_memo)
fibo_memo[n] = nth_fibo
return nth_fibo
print(fibo_dynamic_programming(input, memo))