비동기 프로그래밍 입문
개념
일반적으로 코드는 동기적으로 실행된다. 즉 앞에 있는 코드가 끝나야만 그 다음 코드가 수행이 된다.
// (1)
const a = 1;
// (2)
const b = 2;
// (3)
console.log(`a의 값은 ${a}입니다.`);
// (4)
alert(`hello ${b}!`);
// 항상 코드는 (1) -> (4)의 순서대로 실행이 됩니다.
이러한 방식을 동기적 방식이라 하고, 비동기는 그에 반대되는 방식이다.
비동기(asynchronous)적 방식이란?
실행 중인 코드의 완료 여부와 무관하게 즉시 다음 코드로 넘어가는 방식이며, 대표적으로 setTimeout, addEventListner 등이 있다. 별도의 요청, 실행 대기, 보류 등과 관련된 코드는 모두 비동기적 코드이며 주로 서버 통신 관련 로직에서 사용된다.
무분별하게 작성할 경우 아래와 같이 콜백지옥으로 들어가게 된다.
//0.5초 주기마다 커피 목록을 수집하고 출력
//각 콜백은 커피 이름을 전달하고 목록에 이름을 추가
//문제점 : 들여쓰기 ㄷㄷ, 값 전달의 순서가 아래 -> 위
setTimeout(function (name) {
var coffeeList = name;
console.log(coffeeList);
setTimeout(function (name) {
coffeeList += ', ' + name;
console.log(coffeeList);
setTimeout(function (name) {
coffeeList += ', ' + name;
console.log(coffeeList);
setTimeout(function (name) {
coffeeList += ', ' + name;
console.log(coffeeList);
}, 500, '카페라떼');
}, '카페모카');
}, '아메리카노');
}, '에스프레소');
Promise
이러한 콜백 지옥을 벗어나기 위해 Promise 객체를 사용한다.
개념
공식 문서에는 Promise 객체를 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타낸다고 한다.
왜 이름이 '약속'일까?
비동기 작업이란 수행의 제어권을 제 3자에게 넘겨준 후 작업이 완료되면 안내를 받아 제어권을 이양받는 식의 일처리 방법을 의미한다.
localhost:3000 에서 볼일을 보다가, 버튼을 클릭해서 네이버 서버에 날씨 정보를 달라고 요청하면 약속대로 약 0.3sec가 지나고 나니 데이터가 들어온다.
위 과정에서 localhost에 있을 때의 모든 제어권은 나에게 있다가, 네이버에 api요청을 하는 순간 제어권은 네이버에게 넘어간다. 그 이후 데이터를 전달받는 순간 다시 제어권은 나에게 넘어오게 된다.
비동기 처리는 이처럼 '약속'에 의해 움직이며, 그 약속에 관련된 사항들이 모두 Promise 객체에 담기는 것이다.
promise 객체에 담기는 상태정보
- 대기 : pendiing, 요청한 직후이며 아직 성공(resolve) 또는 실패(rejected)되지 않은 상태
- 이행 : fulfilled, 정상적으로 데이터를 전달하여 resolve한 상태
- 거부 : rejected, 어떠한 이유로 데이터를 전달 하지 못하여 rejected된 상태
객체 핸들링 방법
then ~ catch
// http://api.naver.com/weather/today 로 요청을 한다고 가정합시다.
axios.get('http://api.naver.com/weather/today')
.then(response => {
console.log('정상처리 되었습니다 : ' + response);
})
.catch(error => {
console.log('오류가 발생하였습니다 : ' + error);
})
.finally(()=>{
console.log('항상 실행되는 부분입니다!');
});
async / await
const getWeather = async () => {
try {
const response = axios.get('http://api.naver.com/weather/today');
console.log('정상처리 되었습니다 : ' + response);
} catch (error) {
console.log('오류가 발생하였습니다 : ' + error);
}
}
REST API
개념
REpresentational State Transfer의 약자로서, 어떤 자원에 대해 CRUD를 진행할 수 있게 HTTP Method(GET, POST, PUT, DELETE)를 사용하여 요청을 보내는 것이다.
이 때, 요청을 위한 자원은 특정한 형태로 표현되는데, URI를 통해 정보의 자원을 표현하고, 자원의 행위는 HTTP Method로 명시한다.
- 자원(Resoutce) : URI
- 행위(Verb) : HTTP Method
- 표현(Representations)
특징 및 장단점
특징
1. Sever-Client (서버 - 클라이어느 구조)
2. Stateless (무상태)
3. Cacheable (캐시 처리 가능)
4. Layered System(계층화)
5. Uniform Interface(인터페이스 일관성)
장점
- HTTP 프로토콜의 인프라를 그대로 사용하므로 REST API 사용을 위한 별도의 인프라를 구축할 필요가 없다.
- HTTP 프로토콜의 표준을 최대한 활용하여 여러 추가적인 장점을 함께 가져갈 수 있게 해 준다.
- HTTP 표준 프로토콜에 따르는 모든 플랫폼에서 사용이 가능하다.
- Hypermedia API의 기본을 충실히 지키면서 범용성을 보장한다.
- REST API 메세지가 의도하는 바를 명확하게 나타내어 쉽게 파악할 수 있다.
- 여러 가지 서비스 디자인에서 생길 수 있는 문제를 최소화 한다.
- 서버와 클라이언트의 역할을 명확하게 분리한다.
단점
- 표준이 자체가 존재하지 않아 정의가 필요하다.
- HTTP Method 형태가 제한적이다.
- 브라우저를 통해 테스트할 일이 많은 서비스라면 쉽게 고칠 수 있는 URL보다 Header 정보의 값을 처리해야 하므로 전문성이 요구된다.
- 구형 브라우저에서 호환이 되지 않아 지원하지 않은 동작이 많아 (IE)
예시
RESTful UR이 가르키는 resource는 수행되는 행위가 아니라 객체며, 범주에 맞는 네이밍 컨벤션을 사용해야한다.
네가지 범주로 나누어 보고 그 범주에 맞는 컨벤션을 간단하게 살펴보겠다.
문서(Document)
- 단일 개념 (파일 하나, 객체 인스턴스 데이터베이스 row)
- 단수 사용 (/device-management, /user-management)
http://api.example.com/device-management/managed-devices/{device-id}
http://api.example.com/user-management/users/{id}
http://api.example.com/user-management/users/admin
컬렉션(Collection)
- 서버가 관리하는 리소스 디렉토리
- 서버가 리소스의 URI를 생성하고 관리
- 복수 사용 (/users)
- POST 기반 등록
- ex) 회원 관리 API
http://api.example.com/user-management/users
http://api.example.com/user-management/users/{id}
스토어(Store)
- 클라이언트가 관리하는 자원 저장소
- 클라이언트가 리소스의 URI를 알고 관리
- PUT 기반 등록
- ex) 정적 컨텐츠 관리, 원격 파일 관리
- 복수 사용 (/files)
http://api.example.com/files
http://api.example.com/files/new_file.txt
컨트롤 URI 혹은 컨트롤러(Controller)
- 문서, 컬렉션, 스토어로 해결하기 어려운 추가 프로세스 실행
- 동사를 직접 사용
- GET, POST만 사용할 수 있는 HTTP FORM의 경우 컨트롤 URI(동사로 된 리소스 경로)를 사용
- ex) /members/delete, /members/new
- 동사 사용(/checkout, /play)
일관성
일관된 resoutce (or represetaion) 네이밍 컨벤션과 URI 형식을 사용하면 모호함이 최소화되고 가독성과 지속성이 극대화 된다.
계층 관계 표현을 위해 `\` 을 사용
http://api.example.com/device-management
http://api.example.com/device-management/managed-devices
http://api.example.com/device-management/managed-devices/{id}
http://api.example.com/device-management/managed-devices/{id}/scripts
http://api.example.com/device-management/managed-devices/{id}/scripts/{id}
마지막 문자로 `\`를 사용하면 안된다.
http://api.example.com/device-management/managed-devices/ /* X */
http://api.example.com/device-management/managed-devices /* O */
가독성을 위해 `-` (하이픈)을 사용
//More readable
http://api.example.com/inventory-management/managed-entities/{id}/install-script-location
//Less readable
http://api.example.com/inventory-management/managedEntities/{id}/installScriptLocation
일부 브라우저나 화면에서는 `_` (언더스코어)는 가려질 수 있기 때문에 혼란을 피하기 위해 `-`(하이픈)을 사용
//More readable
http://api.example.com/inventory-management/managed-entities/{id}/install-script-location
//Less readable
http://api.example.com/inventory-management/managedEntities/{id}/installScriptLocation
소문자를 사용
Schem와 HOST에만 대소문자 구별이 없고, 이 외에는 대소문자가 구별된다.
아래 3개의 URL은 대소문자 구별이 없다면 모두 동일한 URL이지만, 1번과 2번은 동일하고 3번은 다르다.
http://api.example.org/my-folder/my-doc //1
HTTP://API.EXAMPLE.ORG/my-folder/my-doc //2
http://api.example.org/My-Folder/my-doc //3
파일 확장자는 사용하면 안된다.
// X
http://api.example.com/device-management/managed-devices.xml
// O
http://api.example.com/device-management/managed-devices
CRUD 함수명은 사용하면 안된다.
URI는 어떤 동작이 수행되는 지 카르키는 게 아니라, 리소스를 가르키는 것이다.
리소스에 대한 작업은 HTTP Method를 이용하도록 한다.
HTTP GET http://api.example.com/device-management/managed-devices //Get all devices
HTTP POST http://api.example.com/device-management/managed-devices //Create new Device
HTTP GET http://api.example.com/device-management/managed-devices/{id} //Get device for given Id
HTTP PUT http://api.example.com/device-management/managed-devices/{id} //Update device for given Id
HTTP DELETE http://api.example.com/device-management/managed-devices/{id} //Delete device for given Id
필터를 위해 쿼리 파라미터를 사용해야 한다.
Resorce(or Reperesentation)에 대한 정렬, 필터링, 페이징은 신규 API를 생성하지 않고 쿼리 파라미터를 활용 하자.
http://api.example.com/device-management/managed-devices
http://api.example.com/device-management/managed-devices?region=USA
http://api.example.com/device-management/managed-devices?region=USA&brand=XYZ
http://api.example.com/device-management/managed-devices?region=USA&brand=XYZ&sort=installation-date
JSON
개념
JSON(JavaScript Object Notation)는 데이터를 저장하거나 전송할 때 많이 사용되는 경량의 DATA 교환 방식이다.
JSON 표현식은 사람과 기계 모두 이해하기 쉬우며 용량이 작아서, XML을 대체하여 데이터 전송 등에 많이 사용된다.
JSON은 데이터 포맷일 뿐이며 어떠한 통신 방법도, 프로그래밍 문법도 아닌 단순히 데이터를 표시하는 표현 방법이다.
특징
서버와 클라이언트 간의 교류에서 일반적으로 많이 사용되며, JS 객체 표기법과 아주 유사하다.
JSON 문서 형식은 JJ객체의 형식을 기반으로 만들어 졌으며, JS를 사용해서 JSON 형식의 문서를 쉽게 JS 객체로 변환할 수 있다.
다른 프로그래밍 언어를 이용해서도 쉽게 만들 수 있으며, 특정 언어에 종속되지 않고 대부분의 프로그래밍 언어에서 JSON 포맷의 데이터를 핸들링 할 수 있는 라이브러리를 제공한다.
구조
JS 객체 형태와 유사할 뿐 완전히 같진 않은데, 작은 따옴표가 아닌 큰 따옴표만 허용된다.
{
"squadName": "Super hero squad",
"homeTown": "Metro City",
"formed": 2016,
"secretBase": "Super tower",
"active": true,
"members": [
{
"name": "Molecule Man",
"age": 29,
"secretIdentity": "Dan Jukes",
"powers": [
"Radiation resistance",
"Turning tiny",
"Radiation blast"
]
},
{
"name": "Madame Uppercut",
"age": 39,
"secretIdentity": "Jane Wilson",
"powers": [
"Million tonne punch",
"Damage resistance",
"Superhuman reflexes"
]
},
{
"name": "Eternal Flame",
"age": 1000000,
"secretIdentity": "Unknown",
"powers": [
"Immortality",
"Heat Immunity",
"Inferno",
"Teleportation",
"Interdimensional travel"
]
}
]
}
메서드
JSON -> 문자열 형태 -> 서버 - 클라이언트 으로 전송 되는데, 다음 두 경우를 위해 파싱(parsing) 과정이 필요하다.
1. JS 객체를 JSON 형태로 전송
2. JSON 형태를 JS 객체 형태로 사용
.stringify()
JS 객체 -> JSON 문자열로 변환 하며 네트워크를 통해 객체를 JSON 문자열로 변환할 때 사용한다.
console.log(JSON.stringify({ x: 5, y: 6 }));
// Expected output: "{"x":5,"y":6}"
console.log(JSON.stringify([new Number(3), new String('false'), new Boolean(false)]));
// Expected output: "[3,"false",false]"
console.log(JSON.stringify({ x: [10, undefined, function(){}, Symbol('')] }));
// Expected output: "{"x":[10,null,null,null]}"
console.log(JSON.stringify(new Date(2006, 0, 2, 15, 4, 5)));
// Expected output: ""2006-01-02T15:04:05.000Z""
.parse()
JSON 문자열 -> JS 객체로 변환하며 네트워크 통신의 결과를 통해 받아온 JSON 문자열을 프로그램 내부에서 사용하기 위해 JS 객체로 변환할 때 사용한다.
const json = '{"result":true, "count":42}';
const obj = JSON.parse(json);
console.log(obj.count);
// Expected output: 42
console.log(obj.result);
// Expected output: true
주의점
AJAX는 단순히 데이터만이 아닌 JS 그 자체도 전달할 수 있는데, 이 말은 JSON 데이터라고 해서 받았는데 단순 데이터가 아니라 JS가 될 수도 있고, 그게 실행 될 수 있다는 것이다.
위와 같은 이유로 받은 내용에서 순수하게 데이터만 추출하기 위한 JSON 관련 라이브러리를 따로 사용하기도 한다.
'항해99 플러스 > React 스터디' 카테고리의 다른 글
[React 스터디] React Router Dom (0) | 2025.03.04 |
---|---|
[React 스터디] React-Query(Tanstack-Query) (0) | 2025.02.25 |
[React 스터디] Redux (0) | 2025.02.24 |
[React 스터디] 2주차 숙련 단계 (0) | 2025.02.17 |
[React 스터디] 2주차 입문 단계 (1) | 2025.02.16 |