juni
Next.js / React ( 타입, json 응답 구조, 토큰, OAuth, postAPI, FormData, JSON 객체, expires ) 본문
Next.js / React ( 타입, json 응답 구조, 토큰, OAuth, postAPI, FormData, JSON 객체, expires )
juni_shin 2024. 11. 11. 18:05json 응답 구조
일반적인 응답 구조
REST API와 같은 일반적인 웹 서비스에서는 보통 다음과 같은 방식으로 응답이 이루어집니다:
- HTTP 상태 코드 (HTTP Status Code):
- 클라이언트가 서버에 요청을 보내면, 서버는 응답과 함께 HTTP 상태 코드를 반환합니다.
- 이 상태 코드는 응답의 성공 여부를 나타내며, 클라이언트는 이를 보고 요청이 성공했는지 실패했는지 판단할 수 있습니다.
- 예시: 200 OK, 404 Not Found, 500 Internal Server Error 등.
- HTTP 상태 코드는 항상 존재하지만, 이는 JSON 응답의 일부가 아니라 HTTP 응답 헤더에서 제공됩니다.
- HTTP 응답 상태 코드(response.status)를 통해 요청 성공 여부를 확인할 수 있습니다.
- statusCode 필드는 JSON 응답에 항상 포함되지 않으며, 서버가 이를 포함하도록 구현한 경우에만 볼 수 있습니다.
타입
1. 기본 사용 목적
- interface: 주로 객체의 구조를 정의하는 데 사용됩니다. 객체의 프로퍼티와 그 타입을 설명할 때 많이 쓰입니다.
- type: 객체뿐만 아니라 기본 타입(alias), 유니언(합집합), 교차 타입(교집합) 등 다양한 타입을 정의할 수 있습니다.
2. 확장성
- interface는 확장 가능: 인터페이스는 상속을 통해 다른 인터페이스를 확장할 수 있습니다. 또한 동일한 이름의 인터페이스를 여러 번 선언하면 자동으로 병합됩니다.
interface Animal {
name: string;
}
interface Animal {
age: number;
}
// 위 두 개의 Animal 인터페이스가 병합됩니다.
const cat: Animal = { name: "Kitty", age: 2 };
- type은 확장 불가: type은 같은 이름으로 여러 번 선언할 수 없으며, 타입 병합이 일어나지 않습니다. 대신 타입 교차(intersection types)를 사용해 확장할 수 있습니다.
type Animal = {
name: string;
};
type Pet = Animal & {
age: number;
};
const cat: Pet = { name: "Kitty", age: 2 };
3. 타입 정의의 범위
- **type**은 훨씬 더 유연합니다. type으로는 유니언 타입, 튜플, 기본 타입의 별칭 등을 정의할 수 있습니다.
type StringOrNumber = string | number; // 유니언 타입
type TupleType = [string, number]; // 튜플 타입
- **interface**는 객체 타입을 정의하는 데 더 적합합니다. 그러나 extends를 통해 여러 인터페이스를 확장할 수 있습니다.
4. 함수 표현
두 가지 모두 함수 타입을 정의할 수 있지만, 인터페이스는 메서드 시그니처를 사용하는 반면, 타입은 함수 표현식을 사용할 수 있습니다.
// type을 사용한 함수 타입 정의
type GreetFunction = (name: string) => string;
// interface를 사용한 함수 타입 정의
interface GreetFunction {
(name: string): string;
}
5. 선택적 사용 권장 사항
- 인터페이스는 주로 객체의 구조를 설명할 때 권장됩니다. 객체의 모양을 정의하고 상속을 통해 확장하거나 병합을 원할 때 유용합니다.
- 타입은 유연성이 더 필요한 경우 사용합니다. 기본 타입을 재정의하거나 유니언 타입, 튜플 등을 정의해야 할 때 좋습니다.
요약
- interface: 객체의 구조를 설명하고, 상속과 병합이 가능.
- type: 객체뿐만 아니라 유니언, 튜플, 기본 타입 등에 더 유연하게 사용 가능. 병합 불가.
토큰
리프레시 토큰의 역할
- 리프레시 토큰(Refresh Token): 일반적으로 액세스 토큰이 만료되었을 때, 인증 서버에 이 토큰을 사용해 새로운 액세스 토큰을 요청하는 데 사용됩니다. 리프레시 토큰은 액세스 토큰보다 보안상 더 오래 사용할 수 있습니다.
OAuth를 통한 인증 과정에서는 액세스 토큰 외에도 다른 토큰들이 사용될 수 있습니다.
예를 들어, OAuth에서는:
- 액세스 토큰: 사용자의 인증 상태를 증명하고, 리소스에 접근할 수 있는 권한을 부여하는 토큰.
- 리프레시 토큰: 만료된 액세스 토큰을 갱신하는 데 사용되는 토큰.
- ID 토큰: 사용자의 식별 정보를 포함하는 토큰 (보통 OpenID Connect에서 사용).
따라서 additional_token은 이 과정에서 발급된 추가적인 인증 토큰일 수 있습니다. 예를 들어, OAuth 서버에서 발급된 추가적인 자격 증명이거나 특정 작업을 위한 임시 토큰입니다.
OAuth 인증
사용자가 자신의 비밀번호를 제공하지 않고도, 다른 애플리케이션이나 웹사이트가 사용자의 리소스에 접근할 수 있도록 허용하는 오픈 표준 프로토콜입니다. 즉, OAuth는 안전하게 제3자 애플리케이션이 사용자의 데이터를 접근할 수 있는 방법을 제공해줍니다.
예시:
OAuth 인증의 가장 흔한 예는, 사용자가 구글(Google)이나 페이스북(Facebook) 같은 플랫폼을 통해 로그인할 때입니다. 이를 통해 사용자는 구글 계정으로 다른 사이트에 로그인하거나, 구글의 특정 데이터를 제3의 애플리케이션에서 사용할 수 있게 할 수 있습니다.
OAuth의 주요 특징
- 비밀번호 제공 없이 권한 부여: 사용자는 구글이나 페이스북 같은 플랫폼에 자신의 비밀번호를 제공하지 않고도, 해당 플랫폼을 통해 다른 애플리케이션에 접근할 수 있는 권한을 부여할 수 있습니다.
- 토큰 기반 인증: OAuth는 **액세스 토큰(access token)**을 사용하여 인증된 사용자임을 증명하고, 애플리케이션이 이 토큰을 사용하여 해당 사용자의 데이터를 접근합니다.
- 유효 기간이 있는 토큰: 액세스 토큰은 일정한 유효 기간이 있으며, 만료되면 리프레시 토큰을 사용하여 새로운 액세스 토큰을 발급받습니다.
OAuth의 작동 방식
OAuth 인증 프로세스는 주로 다음과 같은 단계로 이루어집니다:
- 사용자가 인증 요청:
- 사용자가 제3자 애플리케이션(예: 타 웹사이트 또는 앱)에 로그인하거나, 자신의 데이터를 제공하려 할 때 해당 애플리케이션은 사용자의 OAuth 제공자(예: 구글, 페이스북 등)에 인증을 요청합니다.
- 사용자의 권한 부여:
- 사용자는 OAuth 제공자에게 로그인한 후, 제3자 애플리케이션이 자신의 특정 데이터에 접근하도록 권한을 부여합니다.
- 액세스 토큰 발급:
- OAuth 제공자는 사용자의 권한 부여를 확인하고, 제3자 애플리케이션에게 액세스 토큰을 발급합니다. 이 토큰을 사용해 애플리케이션은 사용자 데이터를 접근할 수 있습니다.
- 리소스에 접근:
- 제3자 애플리케이션은 발급받은 액세스 토큰을 사용해 OAuth 제공자(예: 구글의 API 등)에게 요청을 보내고, 권한이 허용된 범위 내에서 사용자의 데이터를 접근하거나 작업을 수행합니다.
- 토큰 갱신 (선택적):
- 액세스 토큰이 만료되면, 제3자 애플리케이션은 리프레시 토큰을 사용해 새로운 액세스 토큰을 발급받을 수 있습니다.
OAuth의 주요 개념
- 리소스 소유자 (Resource Owner):
- 사용자를 의미합니다. 리소스 소유자는 자신의 리소스(예: 구글 드라이브 파일, 페이스북 프로필 정보 등)에 대한 권한을 제3자 애플리케이션에 부여합니다.
- 클라이언트 (Client):
- 제3자 애플리케이션으로, 사용자의 데이터를 접근하기 위해 OAuth 인증을 요청하는 주체입니다. 예를 들어, 구글 계정으로 로그인하는 웹사이트가 클라이언트 역할을 합니다.
- 권한 부여 서버 (Authorization Server):
- OAuth 제공자(예: 구글, 페이스북)가 사용자를 인증하고, 애플리케이션에 액세스 토큰을 발급해 주는 서버입니다.
- 액세스 토큰 (Access Token):
- 애플리케이션이 사용자 데이터를 접근할 수 있도록 허용하는 짧은 유효 기간을 가진 토큰입니다. 애플리케이션은 이 토큰을 사용해 사용자의 데이터를 요청할 수 있습니다.
- 리프레시 토큰 (Refresh Token):
- 액세스 토큰이 만료되었을 때, 새로운 액세스 토큰을 발급받기 위해 사용하는 긴 유효 기간을 가진 토큰입니다.
OAuth 예시 흐름 (구글 계정으로 로그인):
- 사용자가 웹사이트에서 구글 계정으로 로그인을 클릭합니다.
- 사용자는 구글 로그인 페이지로 이동하여 자신의 구글 계정으로 로그인하고, 해당 웹사이트가 구글 프로필 정보에 접근하도록 권한을 부여합니다.
- 구글은 웹사이트에 액세스 토큰을 발급합니다.
- 웹사이트는 이 액세스 토큰을 사용하여 구글에서 사용자의 프로필 정보를 가져옵니다.
요약:
OAuth 인증은 사용자 비밀번호를 노출하지 않고도 제3자 애플리케이션이 사용자의 데이터를 접근할 수 있도록 권한을 부여하는 안전한 방법입니다. 이를 통해 사용자는 안전하게 다른 웹사이트나 애플리케이션을 이용하면서 자신의 데이터를 공유할 수 있습니다.
postAPI 예시
export async function postApi(
endpoint: string,
body: any,
requireAuth = true,
isFormData = false
) {
const url = new URL(${API_URL}${endpoint});
const headers: any = {};
if (!isFormData) {
headers["Content-Type"] = "application/json";
}
return fetchWithAuth(
url.toString(),
{
method: "POST",
headers,
body: isFormData ? body : JSON.stringify(body),
},
requireAuth,
isFormData // FormData 사용 여부를 fetchWithAuth에 전달
);
}
- 매개변수:
- endpoint: API 요청을 보낼 경로입니다. 기본적으로 API_URL에 붙여서 완전한 URL을 만듭니다.
- body: 요청에 포함될 데이터입니다. JSON 형태로 전송할 수도 있고, FormData 형식으로 보낼 수도 있습니다.
- requireAuth (기본값: true): 인증이 필요한지 여부를 나타냅니다. 인증이 필요한 경우, fetchWithAuth 함수에서 적절한 처리를 할 것으로 보입니다.
- isFormData (기본값: false): 데이터가 FormData 형식인지 아닌지를 나타냅니다. FormData 형식이라면, 본문을 그대로 사용하고, 아니라면 JSON으로 변환해 전송합니다.
- URL 구성:
- const url = new URL(${API_URL}${endpoint});: API_URL과 endpoint를 결합하여 완전한 API URL을 만듭니다.
- 헤더 설정:
- const headers: any = {};: 헤더 객체를 만듭니다.
- if (!isFormData) { headers["Content-Type"] = "application/json"; }: FormData 형식이 아닌 경우, 요청 본문이 JSON임을 나타내기 위해 Content-Type 헤더를 application/json으로 설정합니다.
- fetchWithAuth 함수 호출:
- fetchWithAuth라는 함수가 API 호출을 처리하는데 사용됩니다. 여기서 POST 요청 방식, 헤더, 본문 등을 설정하여 API에 요청을 보냅니다.
- isFormData가 true이면, 본문(body)을 그대로 보내고, false인 경우 JSON.stringify(body)로 변환해서 전송합니다.
- 인증 여부 전달:
- requireAuth가 true이면, fetchWithAuth가 인증 처리를 할 가능성이 큽니다. 이 값을 전달하여 인증이 필요한 API 요청인지 여부를 제어합니다.
FormData
JSON 객체와는 다릅니다. 둘은 다른 목적으로 사용되는 데이터 형식입니다.
1. FormData
- FormData는 주로 HTML 폼 데이터를 전송할 때 사용되는 객체입니다.
- 키-값 쌍으로 데이터를 관리하며, 주로 파일 업로드나 폼 필드와 같은 데이터를 서버로 전송하는 데 사용됩니다.
- 파일(이미지, 문서 등)과 같은 바이너리 데이터를 전송할 수 있는 특징이 있습니다.
- MIME 타입은 multipart/form-data입니다.
const formData = new FormData();
formData.append('name', 'Alice');
formData.append('file', someFile); // 파일 전송 가능
fetch('/submit', {
method: 'POST',
body: formData,
});
2. JSON 객체
- JSON(JavaScript Object Notation)은 문자열 기반의 데이터 형식으로, 주로 구조화된 데이터를 전송하거나 저장할 때 사용됩니다.
- 파일 같은 바이너리 데이터를 포함할 수 없으며, 텍스트 데이터만 처리합니다.
- MIME 타입은 application/json입니다.
const jsonData = {
name: 'Alice',
age: 25,
};
fetch('/submit', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(jsonData),
});
차이점 요약:
- FormData: 파일이나 폼 데이터를 서버로 전송할 때 사용하며, 파일 업로드를 지원합니다.
- JSON 객체: 주로 구조화된 데이터(텍스트)를 서버로 전송할 때 사용되며, 파일 전송은 지원하지 않습니다.
const expires = new Date(); expires.setDate(expires.getDate() + 7);
쿠키나 기타 데이터의 만료 날짜를 설정하는 데 자주 사용되며, 현재 날짜에서 7일 후의 날짜를 계산하는 것입니다.