진행중인 프로젝트에서 42 API로 데이터를 받을 일이 생겼는데 생각했던 것 보다 훨씬 더 어려웠다. 기존의 레퍼런스들이 있긴 했지만 fetch로 구현된 것은 찾을 수가 없었다...ㅠ 까다로운 작업이 될 것 같아서 확실하게 개념을 잡고 가기 위해 이렇게 정리를 하기로 했다.
응용 프로그램에서 사용할 수 있도록 운영체제나 프로그램이 언어가 제공하는 기능을 제어할 수 있게 만든 인터페이스, 접점이다.
인터페이스 : 기계와 기계, 기계와 인간 사이의 소통 방식, 접점을 의미한다.
유저 인터페이스(UI) : 사용자가 소통을 하기 위한 접점. 하드웨어로는 스크린, 모니터, 키보드, 소프트웨어로는 UI가 인터페이스에 포함된다. 자동차 문을 열기 위해 열림 버튼을 누르는 것, 배달 앱을 이용해 주문하는 것 역시 UI이다.
API : 애플리케이션에서 데이터를 읽거나 쓰기 위해 사용하는 인터페이스. 기계와 기계, 소프트웨어와 소프트웨어 사이에서의 지정된 형식(format)으로 요청, 명령을 받을 수 있는 수단이다. print, printf, document.write도 API라고 할 수 있다. 공개 여부에 따라 Public, Privite API로 구분된다.
HTTP API : 인터넷에서 데이터를 주고받을 수 있는 프로토콜(통신 규약, 규칙)인 HTTP의 규칙을 준수하는 인터페이스.
https://jeong-pro.tistory.com/180
REST는 웹에서 데이터를 전송하고 처리하는 방법을 정의한 인터페이스다.
정보를 주고 받는 데 있어서 널리 쓰이는 형식이며, 각 요청이 어떤 동작이나 정보를 위한 것인지 요청 자체로 추론할 수 있다.
REST는 제약 조건의 집합이라고도 할 수 있는데, 이 조건들을 모두 준수하면 RESTful하다 라고 말할 수 있다.
https://usefultoknow.tistory.com/entry/JSON이란-무엇일까
객체를 만들 때 사용하는 표현식을 의미한다.
key:value 형식으로 이루어진 데이터 형식인데, 사람과 기계 모두 이해하기 쉽고 용량도 작아서 데이터 전송에 많이 사용된다.
자바스크립트의 문법을 따르지만 다른 프로그래밍 언어에서도 사용가능하다.
자바스크립트 객체처럼 중괄호를 이용해서 key:value로 표현한다. ,
로 값을 나열한다.
https://ko.wikipedia.org/wiki/OAuth
https://d2.naver.com/helloworld/24942
https://velog.io/@undefcat/OAuth-2.0-간단정리
https://velog.io/@piecemaker/OAuth2-인증-방식에-대해-알아보자
인터넷 사용자들이 비밀번호를 제공하지 않고/ 다른 웹사이트 상의 자신들의 정보에 대해/ 웹사이트나 애플리케이션의 접근 권한을 부여할 수 있는 공통적인 수단/으로서 사용되는, 접근 위임을 위한 [개방형 표준]이다.
위키 백과 발췌.
당신은 어떤 예약 서비스를 제공하는 앱 개발자이며, 카카오 계정 연동을 구현해야 한다. 우리 서비스를 구현하려는 고객이 어떻게 카카오 회원임을 알 수 있을까? 일차원적으로 생각하면 사용자에게서 아이디와 비밀번호를 입력받고, 그것을 이용해 실제로 로그인 하는 방법이 있을 것이다. 그러나 어떤 고객이 개발자를 믿고 자신의 계정을 홀랑 넘겨주겠는가? 기각이다. 계정 정보를 받지 않고도 고객에 대한 정보를 얻을 수 있는 방법이 없을까? 이를 가능케하는 것이 OAuth라는 프로토콜이다.
기존에도 다른 애플리케이션에 사용자의 아이디와 암호가 노출되지 않도록 하면서 API에 접근할 수 있는 인증 방법이 몇몇 있었다. OAuth 1.0은 제각각인 인증방식을 표준화한 인증방식이다. OAuth를 이용하면 이 인증을 공유하는 애플리케이션끼리는 별도의 인증이 필요없어지고, 따라서 여러 애플리케이션을 통합하여 사용하는 것이 가능하게 된다. 1.0보다 인증 절차를 간략화 시킨 것이 2.0이다.
Access Token은 임의의 문자열 값이다. 고객이 카카오에 로그인을 하면 카카오는 Access Token을 발급해준다. URL에 쿼리스트링으로 넘어온 토큰을 카카오에 넘겨주면 카카오는 토큰을 검증한 후 해당 고객에 대한 정보를 제공해준다. 이를 이용해 우리는 사용자에 대한 정보를 얻을 수 있다.
https://api.intra.42.fr/apidoc
https://42kchoi.tistory.com/148
https://hi0seon.tistory.com/entry/springboot-42-API-OAuth사용하기
https://developing-sunny.tistory.com/13
The API is RESTful, uses JSON over HTTPS and lets you authenticate users with OAuth 2.0.
42 api 메인에 띄워진 문구다. RESTful API고, JSON 형식으로 값을 리턴하고, OAuth 2.0 방식으로 유저 인증을 한다고 한다.
42 API는 다른 API처럼 그냥 GET, POST 할 수가 없다. OAuth 2.0 방식으로 42 카뎃들만 사용할 수 있게 인증을 받고 있기에, 사용하려면 엑세스 토큰을 발급받아야만 하다.
엑세스 토큰을 발급받기 위해서는 우선 42 인트라넷에서 API를 생성해야 한다. https://42kchoi.tistory.com/148 링크에서 자세히 설명하고 있기에 간단히만 짚고 넘어가겠다.
다른 거야 아무렇게나 써도 되지만 Redirect URI는 꽤나 중요하다. 42 api를 통해 인증이 성공했을 때 리다이렉트 되는 주소인데, 이 주소로 리다이렉트 될 때 UI을 살펴보면 뒤에 http://localhost:3000/mypage?code=c970617118d3925fe38980ac27c8ae140c2d274a0649a036cff6681728ee94d7
같이 문자열이 들어오게 된다. 해당 문자열을 가지고 액세스 토큰을 발급받으니 서버단에서 파싱해서 받을 수 있게끔 해야 한다. 코드가 없어도 제한적이지만 API 사용은 가능하다.
입력을 마치면 UID, SECRET, REDIRECT URI가 당신을 맞이해준다. UID, SECRET은 우리가 토큰을 받을 때 요청한 사용자가 믿을 만한지(카뎃이 맞는지)를 확인하기 위해 쓰일 것이다. REDIRECT URI의 경우 위에서 언급했듯이 인증이 성공했을 때 사용자를 보내줄 주소이다. 홈페이지와 같은 주소로 넣어버리자.
액세스 토큰을 발급받기 위한 준비를 모두 마쳤다. 필자는 fetch를 이용해서 HTTP 통신을 진행할 것이다.
fetch(url, {
method: ,
headers: {
},
method에는 GET, POST, PUT, DELETE같은 HTTP 메서드가 들어간다. method를 설정하지 않을 경우 기본값인 GET이 들어간다.
headers에는 HTTP 헤더의 내용이 들어간다. 부가적인 정보를 전송할 때 headers에 넣으면 되는데, 42API에서 GET을 할 때 부가적이라기에는 너무나도 중요한 액세스 코드를 headers에 넣고 요청을 보내게 된다.
const url = 'https://api.intra.42.fr/oauth/token';
const query =
'?' +
'grant_type=' +
'client_credentials' +
'&' +
'client_id=' +
'UID' +
'&' +
'client_secret=' +
'SECRET' +
'&' +
'redirect_uri=' +
'REDIRECT URI' +
'&' +
'scope=public';
fetch(url + query, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'X-Mobile': 'false',
'response-Type': 'text',
},
})
.then(res => res.json())
.then(res => console.log(res));
수많은 삽질의 결과
url 뒤에 쿼리스트링을 붙여서 POST로 전송을 한다. 쿼리스트링은 다음의 양식에 API를 만든 페이지의 UID, SECRET, REDIRECT URI를 그대로 대입하면 된다. 리다이렉트하면서 url로부터 코드를 가지고 있고 사용하겠다면 'client_credentials'
를 'authorization_code'
로 바꿔주고 "code=" + "코드" + "&"
구문을 client_secret
과 redirect_uri
사이에 추가해주면 된다.
특이하게 headers을 제거해도 정상적으로 작동하고 있었다. 필수는 아닌 모양이다.
access_token: "d7e66f8056047e2a233afbb40c69609c79fae2a95c020efc7ef48dc414540075"
created_at: 1630994872
expires_in: 644
scope: "public"
token_type: "bearer"
몇 번의 오류를 겪으며 성공적으로 POST 했다면 다음과 같은 응답을 받게 된다. access_token의 값이 우리가 그토록 원하던 엑세스 토큰이다!
https://api.intra.42.fr/apidoc
위의 링크에 우리가 사용할 수 있는 온갖 API들이 들어있다. 그 중 적당한 것을 골라 실제로 사용해보자. 다음 API에서 42 서울의 카뎃들에 대한 정보를 뽑아올 것이다.
let token;
const bearer = 'Bearer ';
const getToken = res => {
console.log(res);
token = res.access_token;
};
const getToken = res => {
console.log(res);
token = res.access_token;
};
fetch(url + query, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'X-Mobile': 'false',
'response-Type': 'text',
},
})
.then(res => res.json())
.then(res => getToken(res));
fetch('https://api.intra.42.fr/v2/campus/29/users', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: bearer + token,
'response-Type': 'text',
},
}),
)
.then(res => res.json())
.then(res => console.log(res));
getToken을 통해 token에 POST를 해서 얻은 액세스 토큰이 들어있다. 42 API로부터 GET을 할 때 헤더에서 Authorization을 통해 인증을 하게 된다. Authorization은 OAuth를 위해 고안된 인증 방법이다. 'Bearer '
문자열 뒤에(공백이 존재하는 거 맞다!) 우리의 액세스 토큰을 넣어주면 문제없이 42 API로부터 데이터를 요청할 수 있게 된다.