API 공부하기 - 공부 및 큰 틀 공부하기
API란 무엇일까요?
**API(Application Programming Interface)**는 애플리케이션들이 서로 소통할 수 있도록 돕는 매개체이자 약속입니다. 쉽게 비유하자면, 식당에서 손님이 메뉴를 보고 주문하면 주방장이 요리해서 가져다주는 과정과 같아요. 여기서 손님은 프론트엔드, 주방장은 백엔드, 메뉴판이 API, 주문하는 행위가 API 요청, 요리가 API 응답이라고 생각하시면 됩니다.
프론트엔드 개발자는 API를 통해 백엔드(서버)에 데이터를 요청하거나, 데이터를 저장하거나, 수정하거나, 삭제하는 등의 작업을 수행합니다.
API 통신의 기본 흐름
프론트엔드와 백엔드의 API 통신은 주로 다음과 같은 흐름으로 이루어집니다.
- 클라이언트(프론트엔드)가 서버(백엔드)에 API 요청을 보냅니다. (예: "상품 목록 주세요!")
- 서버는 요청을 받아서 처리합니다. (예: 데이터베이스에서 상품 목록을 조회합니다.)
- 서버는 처리 결과를 클라이언트에게 응답으로 보냅니다. (예: 상품 목록 데이터를 JSON 형태로 전달합니다.)
- 클라이언트(프론트엔드)는 응답을 받아서 화면에 보여주거나 다른 작업을 수행합니다.
API 관련 필수 개념들
이제 API 통신에 필요한 핵심 개념들을 하나씩 살펴보겠습니다.
1. HTTP 메서드 (Methods)
API 요청 시 어떤 종류의 작업을 수행할 것인지 나타내는 약속입니다. 주로 다음과 같은 메서드들이 사용됩니다.
- GET: 데이터를 조회할 때 사용합니다. (예: 게시글 목록 가져오기)
- POST: 새로운 데이터를 생성하거나 전송할 때 사용합니다. (예: 회원가입, 게시글 작성)
- PUT: 기존 데이터를 모두 업데이트할 때 사용합니다.
- PATCH: 기존 데이터의 일부만 업데이트할 때 사용합니다.
- DELETE: 데이터를 삭제할 때 사용합니다.
2. URL (Uniform Resource Locator) / Endpoint (엔드포인트)
API 통신에서 URL은 특정 리소스(자원)의 위치를 나타냅니다. 특히 API에서는 이 URL을 엔드포인트라고 부르기도 합니다. 엔드포인트는 API가 제공하는 특정 기능에 접근하기 위한 주소입니다.
- 예시: https://api.example.com/products (모든 상품 조회), https://api.example.com/products/123 (ID가 123인 상품 조회)
3. 요청(Request)과 응답(Response)
API 통신은 항상 **요청(Request)**과 **응답(Response)**의 형태로 이루어집니다.
요청(Request)
프론트엔드가 백엔드에 보내는 정보입니다. 다음 요소들을 포함할 수 있습니다.
- URL/Endpoint: 어떤 API를 호출할 것인지
- HTTP 메서드: 어떤 작업을 수행할 것인지 (GET, POST 등)
- 헤더(Header): 요청에 대한 추가 정보. (자세한 내용은 아래에서 다룹니다.)
- 바디(Body): 요청에 포함될 실제 데이터 (POST, PUT, PATCH 메서드에 주로 사용)
응답(Response)
백엔드가 프론트엔드에 보내는 처리 결과입니다. 다음 요소들을 포함할 수 있습니다.
- HTTP 상태 코드(Status Code): 요청 처리 결과를 숫자로 나타냅니다. (자세한 내용은 아래에서 다룹니다.)
- 헤더(Header): 응답에 대한 추가 정보.
- 바디(Body): 요청 처리 결과로 전달되는 실제 데이터. 주로 JSON 형태로 제공됩니다.
4. HTTP 상태 코드 (Status Codes)
서버가 클라이언트의 요청을 성공적으로 처리했는지, 아니면 오류가 발생했는지 알려주는 3자리 숫자 코드입니다. 몇 가지 중요한 상태 코드는 다음과 같습니다.
- 200 OK: 요청이 성공적으로 처리되었습니다.
- 201 Created: 새로운 리소스가 성공적으로 생성되었습니다. (주로 POST 요청 성공 시)
- 400 Bad Request: 클라이언트의 요청이 잘못되었습니다. (예: 필수 파라미터 누락)
- 401 Unauthorized: 인증되지 않은 사용자입니다. (로그인이 필요할 때)
- 403 Forbidden: 접근 권한이 없습니다. (인증은 되었지만, 해당 리소스에 대한 접근 권한이 없을 때)
- 404 Not Found: 요청한 리소스(URL)를 찾을 수 없습니다.
- 500 Internal Server Error: 서버 내부에서 오류가 발생했습니다.
5. 헤더(Header)
요청과 응답에 대한 메타 정보(추가 정보)를 담는 부분입니다. 다양한 정보가 담길 수 있으며, 몇 가지 중요한 헤더는 다음과 같습니다.
- Content-Type: 바디에 담긴 데이터의 타입 (예: application/json, text/html)
- Authorization: 인증 정보 (토큰 등)를 담아 서버에 보낼 때 사용됩니다.
- Accept: 클라이언트가 받을 수 있는 응답 데이터 타입 지정 (예: application/json)
- User-Agent: 요청을 보낸 클라이언트(브라우저, 앱) 정보
- Cache-Control: 캐싱 관련 지시자
6. JSON (JavaScript Object Notation)
API 통신에서 데이터를 주고받는 데 가장 널리 사용되는 형식입니다. 자바스크립트 객체와 유사한 형태로 데이터를 표현하며, 가볍고 사람이 읽기 쉬워서 웹에서 데이터를 교환하는 데 최적화되어 있습니다.
{
"id": 1,
"name": "멋진 상품",
"price": 10000,
"description": "이것은 정말 멋진 상품입니다."
}
7. 인증(Authentication)과 인가(Authorization)
보안과 관련된 매우 중요한 개념입니다.
- 인증(Authentication): "당신이 누구인지"를 확인하는 과정입니다. 로그인 시 아이디와 비밀번호를 통해 사용자를 확인하는 것이 대표적인 예시입니다.
- 인가(Authorization): "당신이 무엇을 할 수 있는지"를 확인하는 과정입니다. 인증된 사용자가 특정 자원에 접근하거나 특정 작업을 수행할 권한이 있는지 확인합니다.
8. 토큰(Token)과 JWT (JSON Web Token)
인증 과정을 거친 사용자에게 서버에서 발급하는 '증명서' 같은 것입니다. 사용자는 이 토큰을 다음 API 요청 시 헤더에 담아 보내면, 서버는 토큰을 통해 사용자를 다시 인증할 필요 없이 바로 인가 과정을 거쳐 작업을 처리합니다.
**JWT(JSON Web Token)**는 토큰의 한 종류로, JSON 형식으로 정보를 담고 암호화되어 있어 위변조를 막을 수 있습니다. 주로 다음과 같은 세 부분으로 구성됩니다.
- Header: 토큰 타입(JWT)과 해싱 알고리즘 정보
- Payload: 사용자 정보(ID, 이름 등)나 토큰 발행 시간, 만료 시간 등 실제 데이터
- Signature: Header와 Payload를 인코딩한 값과 비밀키를 이용해 생성된 서명. 이 서명으로 토큰의 위변조 여부를 확인할 수 있습니다.
JWT는 주로 Authorization 헤더의 Bearer 타입으로 서버에 전달됩니다.
Authorization: Bearer <YOUR_JWT_TOKEN>
Authorization: Bearer <YOUR_JWT_TOKEN>
*Bearer (베어러)
Bearer는 OAuth 2.0 표준에서 정의된 토큰 타입(Token Type) 중 하나입니다. 주로 **액세스 토큰(Access Token)**과 함께 사용됩니다.
가장 중요한 의미는 다음과 같습니다:
"이 토큰을 가지고 있는 사람(bearer)이 인증된 사용자이며, 이 토큰을 제시하면 접근 권한이 주어진다."
쉽게 말해, "나는 이 토큰을 가지고 있으니, 나에게 권한을 줘!" 라고 말하는 것과 같습니다. 이 토큰을 소유하고 있는 것만으로도 권한을 부여받는다는 의미입니다.
Authorization 헤더에서 Bearer의 역할
프론트엔드에서 API 요청을 보낼 때, 인증된 사용자임을 알리기 위해 HTTP 요청 헤더에 토큰을 포함시킵니다. 이때 Authorization 헤더는 다음과 같은 형식으로 사용됩니다.
Authorization: Bearer <YOUR_ACCESS_TOKEN_HERE>
- Authorization: 이 헤더는 클라이언트의 인증 자격 증명(credentials)을 서버에 전달하는 데 사용됩니다.
- Bearer: 토큰의 타입을 명시합니다. 서버는 이 Bearer라는 타입을 보고, 뒤에 오는 <YOUR_ACCESS_TOKEN_HERE> 부분이 액세스 토큰이며, 이 토큰의 소유자에게 권한을 부여해야 한다는 것을 이해합니다.
- <YOUR_ACCESS_TOKEN_HERE>: 실제 JWT와 같은 액세스 토큰 문자열이 들어가는 부분입니다.
Bearer 토큰의 특징과 주의사항
- 간단함: 구현하기 쉽고 널리 사용됩니다.
- 자기 포함적: 토큰 자체에 필요한 정보(사용자 ID, 만료 시간 등)가 포함되어 있어, 서버가 매번 데이터베이스를 조회할 필요 없이 토큰만으로 유효성을 검증할 수 있습니다. (JWT의 경우)
- 주의사항 (탈취 시 위험): Bearer 토큰은 토큰 자체를 탈취당하면, 해당 토큰의 수명이 다할 때까지 토큰을 탈취한 공격자가 정당한 사용자처럼 행동할 수 있습니다. 따라서 토큰은 항상 보안에 유의하여 다루어져야 하며, HTTPS와 같은 암호화된 통신 채널을 통해서만 전송되어야 합니다. 또한, 짧은 만료 시간과 리프레시 토큰 사용으로 이러한 위험을 줄이는 것이 일반적입니다.
토큰은 보안상의 이유로 **만료 시간(Expiration Time)**을 가지고 있습니다.
토큰에 수명이 있는 이유
- 보안 강화: 토큰이 영원히 유효하다면, 만약 토큰이 탈취되었을 경우 공격자가 해당 토큰으로 영구적으로 사용자 계정에 접근할 수 있게 됩니다. 만료 시간을 설정하면, 토큰이 탈취되더라도 일정 시간 후에는 무효화되어 피해를 줄일 수 있습니다.
- 사용자 세션 관리: 토큰의 수명을 통해 사용자의 로그인 세션을 관리할 수 있습니다. 예를 들어, 일정 시간 동안 활동이 없으면 토큰이 만료되어 자동으로 로그아웃되도록 하여 보안을 유지하고 불필요한 세션을 정리할 수 있습니다.
- 권한 변경 반영: 사용자의 권한이 변경되었을 때, 만료된 토큰은 더 이상 유효하지 않으므로 새로운 토큰을 발급받아야 합니다. 이 과정에서 변경된 권한이 새 토큰에 반영되어 적용될 수 있습니다.
토큰 수명 관리 방법
일반적으로 토큰의 수명은 서버에서 결정하며, 토큰의 payload 부분에 exp (expiration time)와 같은 필드로 만료 시간이 기록됩니다.
프론트엔드에서는 토큰을 받은 후, 이 토큰을 저장하고 API 요청 시 사용합니다. 만약 토큰이 만료되었거나 유효하지 않다는 응답(예: HTTP 상태 코드 401 Unauthorized)을 서버로부터 받으면, 다음과 같은 방법으로 처리합니다.
- 토큰 갱신 (Refresh Token): 많은 서비스에서는 짧은 수명의 **액세스 토큰(Access Token)**과 긴 수명의 **리프레시 토큰(Refresh Token)**을 함께 사용합니다. 액세스 토큰이 만료되면, 리프레시 토큰을 사용하여 서버에 새로운 액세스 토큰을 요청하여 발급받습니다. 이렇게 하면 사용자가 매번 로그인할 필요 없이 편리하게 서비스를 이용할 수 있으면서도 보안을 유지할 수 있습니다.
- 재로그인 유도: 리프레시 토큰마저 만료되었거나, 리프레시 토큰을 사용하지 않는 시스템이라면, 사용자에게 재로그인을 요청하여 새로운 토큰을 발급받도록 안내합니다.
이처럼 토큰은 단순히 인증 정보를 담고 있는 것이 아니라, 보안과 사용자 경험을 고려하여 수명이 관리되는 중요한 요소입니다.
9. 인스턴스 (Instance) / 클라이언트 라이브러리
프론트엔드에서 API 요청을 편리하게 보내기 위해 사용하는 도구나 라이브러리를 통칭하는 개념으로, 보통 HTTP 클라이언트 라이브러리를 의미합니다. 대표적으로 Axios나 브라우저 내장 Fetch API가 있습니다.
- Axios: Promise 기반의 HTTP 클라이언트로, 요청 및 응답 가로채기, 에러 핸들링 등 다양한 기능을 제공하여 API 통신을 더욱 편리하게 만들어 줍니다.
- Fetch API: 웹 브라우저에 내장된 HTTP 요청 함수로, 별도의 라이브러리 설치 없이 사용할 수 있습니다.
이러한 라이브러리를 사용하여 API 요청을 보낼 때, 한 번 설정해둔 공통적인 설정을 여러 요청에서 재사용하기 위해 인스턴스를 생성하여 사용하는 경우가 많습니다. 예를 들어, baseURL (기본 API 서버 주소)이나 headers (기본 헤더) 등을 인스턴스에 설정해두면 매번 반복해서 작성할 필요가 없어집니다.
💡HTTP 클라이언트 라이브러리 (Axios 인스턴스):
프론트엔드에서 API 요청을 편리하게 보내기 위해 사용하는 도구입니다.
Axios 인스턴스 를 통해 baseURL 이나 headers 같은 공통 설정을 미리 해둘 수 있습니다.
// Axios 인스턴스 생성 예시
import axios from 'axios';
const api = axios.create({
baseURL: 'https://api.example.com', // 기본 API 서버 주소
timeout: 10000, // 10초 타임아웃
headers: {
'Content-Type': 'application/json',
// 'Authorization': `Bearer ${localStorage.getItem('token')}` // 토큰이 있다면 기본적으로 추가
}
});
// 이 인스턴스를 사용하여 API 요청
api.get('/products')
.then(response => console.log(response.data))
.catch(error => console.error(error));
프론트엔드에서 API를 다루는 흐름 (튜토리얼처럼!)
1단계: API 명세서 확인하기
백엔드 개발자가 제공하는 **API 명세서(API Documentation)**를 확인하는 것이 가장 먼저 할 일입니다. 이 명세서에는 어떤 API가 있고, 각 API가 어떤 기능을 하며, 어떤 URL과 HTTP 메서드를 사용하고, 어떤 요청 바디/파라미터를 보내야 하고, 어떤 응답을 주는지 등 모든 정보가 담겨 있습니다.
2단계: HTTP 클라이언트 선택 및 설정
Axios나 Fetch API 중 하나를 선택합니다. 대부분의 경우 Axios가 더 편리한 기능을 많이 제공하므로 추천합니다.
# Axios 설치 (React, Vue 등에서)
npm install axios
# 또는
yarn add axios
그리고 앞서 설명한 것처럼 Axios 인스턴스를 생성하여 기본적인 설정을 해두는 것이 좋습니다.
// src/api/index.js (예시 파일)
import axios from 'axios';
const api = axios.create({
baseURL: 'https://api.your-service.com', // 실제 API 서버 주소로 변경하세요!
timeout: 15000, // 15초 타임아웃
headers: {
'Content-Type': 'application/json',
},
});
export default api;
3단계: 사용자 인증 (로그인/회원가입) 처리
대부분의 서비스는 로그인이 필요합니다.
1. 로그인 페이지: 사용자가 아이디와 비밀번호를 입력합니다.
2 .로그인 API 요청 (POST): 프론트엔드는 입력받은 아이디와 비밀번호를 바디에 담아 로그인 API(예: /auth/login)에 POST 요청을 보냅니다.
import api from '../api'; // 위에서 만든 api 인스턴스
async function login(username, password) {
try {
const response = await api.post('/auth/login', { username, password });
const { token } = response.data; // 서버에서 토큰을 응답으로 줍니다.
localStorage.setItem('token', token); // 토큰을 로컬 스토리지에 저장
// 로그인 성공 처리 (예: 페이지 이동)
console.log('로그인 성공!', token);
return true;
} catch (error) {
console.error('로그인 실패:', error);
// 로그인 실패 처리 (예: 에러 메시지 표시)
return false;
}
}
3. 토큰 저장: 서버는 로그인 성공 시 JWT 같은 토큰을 응답으로 줍니다. 프론트엔드는 이 토큰을 localStorage나 sessionStorage에 저장합니다.
4. Axios 인스턴스에 토큰 추가: 이후의 모든 API 요청에 이 토큰을 Authorization 헤더에 자동으로 담아 보내도록 Axios 인스턴스를 수정합니다.
// src/api/index.js 에 추가 또는 수정
api.interceptors.request.use((config) => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
}, (error) => {
return Promise.reject(error);
});
이 코드는 모든 요청이 서버로 보내지기 전에 실행되는 인터셉터입니다. 로컬 스토리지에 토큰이 있다면 요청 헤더에 Authorization을 추가해줍니다.
근데 이런 일련의 흐름을 보다보니 생각난 것
🚨 저장하고 git ignore🚨 에 꼭 추가 해줘야 할 것 !
4단계: 인증된 API 요청 보내기 (데이터 조회)
이제 로그인 후 토큰이 있다면, 인증이 필요한 API에 요청을 보낼 수 있습니다.
// 상품 목록을 가져오는 예시
import api from '../api';
async function getProducts() {
try {
const response = await api.get('/products'); // GET 요청
console.log('상품 목록:', response.data);
return response.data;
} catch (error) {
if (error.response && error.response.status === 401) {
console.error('인증 실패! 다시 로그인해주세요.');
// 토큰 만료 또는 유효하지 않은 토큰일 경우 로그인 페이지로 리다이렉트
localStorage.removeItem('token');
// window.location.href = '/login'; // 실제 앱에서는 라우터를 사용
} else {
console.error('상품 목록 가져오기 실패:', error);
}
return null;
}
}
// 사용 예시
getProducts();
여기서 error.response.status === 401은 토큰이 유효하지 않거나 만료되었을 때 서버가 401 Unauthorized 상태 코드를 보낼 수 있다는 것을 의미합니다. 이때는 토큰을 제거하고 다시 로그인하도록 안내해야 합니다.
5단계: 데이터 생성/수정/삭제
마찬가지로 POST, PUT/PATCH, DELETE 메서드를 사용하여 데이터를 조작합니다.
JavaScript
// 새로운 상품 생성 (POST)
async function createProduct(productData) {
try {
const response = await api.post('/products', productData); // productData는 JSON 객체
console.log('상품 생성 성공:', response.data);
return response.data;
} catch (error) {
console.error('상품 생성 실패:', error);
return null;
}
}
// 상품 정보 업데이트 (PUT/PATCH)
async function updateProduct(productId, updateData) {
try {
const response = await api.put(`/products/${productId}`, updateData); // PUT
// const response = await api.patch(`/products/${productId}`, updateData); // PATCH (부분 업데이트)
console.log('상품 업데이트 성공:', response.data);
return response.data;
} catch (error) {
console.error('상품 업데이트 실패:', error);
return null;
}
}
// 상품 삭제 (DELETE)
async function deleteProduct(productId) {
try {
await api.delete(`/products/${productId}`);
console.log('상품 삭제 성공');
return true;
} catch (error) {
console.error('상품 삭제 실패:', error);
return false;
}
}
추가적으로 알아두면 좋은 개념
- CORS (Cross-Origin Resource Sharing): 웹 브라우저 보안 정책 중 하나로, 한 도메인에서 다른 도메인으로 HTTP 요청을 보낼 때 발생하는 문제입니다. 백엔드에서 CORS 설정을 해주어야 프론트엔드에서 API 요청을 보낼 수 있습니다. Access-Control-Allow-Origin 헤더와 관련이 있습니다.
- RESTful API: API를 설계하는 일종의 아키텍처 스타일입니다. URL을 통해 리소스를 명확하게 나타내고, HTTP 메서드를 통해 해당 리소스에 대한 CRUD(Create, Read, Update, Delete) 작업을 수행하는 방식입니다. 대부분의 현대 API는 RESTful 원칙을 따릅니다.
- GraphQL: RESTful API의 대안으로 떠오르는 쿼리 언어입니다. 필요한 데이터를 정확히 요청하고 응답받을 수 있어, 여러 번의 API 호출 없이 한 번의 요청으로 필요한 모든 데이터를 가져올 수 있다는 장점이 있습니다.
- Swagger/OpenAPI: API 명세서를 자동으로 생성하고 시각화해주는 도구입니다. 백엔드 개발자가 이를 활용하면 프론트엔드 개발자는 훨씬 쉽게 API를 이해하고 사용할 수 있습니다.