Axios
는 브라우저, Node.js
를 위한 HTTP 비동기 통신 라이브러리입니다.
프로젝트에 axios
를 설치하는 방법은 여러가지가 있지만 우리는 yarn
을 이용하여 설치하겠습니다.
npm install axios
or
yarn add axios
비동기 통신이 성공했을 경우, .then()
은 콜백을 인자로 받아 결과값을 처리할 수 있습니다.
.catch()
를 통해 오류를 처리합니다.
error
객체에서는 오류에 대한 주요 정보를 확인할 수 있습니다.
axios.get('/hello').
catch((error) => {
if(error.response) {
console.log(error.response.status);
console.log(error.response.headers);
}
})
위와 같이 .catch
에서 받아오는 error
객체를 통해 error.response.status
응답 상태 코드와 error.response.headers
응답 헤더 정보를 파악할 수 있습니다.
서버에서 데이터를 가져올 때 사용하는 메서드입니다.
두번째 파라미터 config
객체에는 헤더(header), 응답 초과시간(timeout), 인자값(params) 등의 요청 값을 같이 넘길 수 있습니다.
서버에 데이터를 새로 생성할 때 사용하는 메서드입니다.
두번째 파라미터로 생성할 데이터를 넘깁니다.
특정 데이터를 수정할 때 요청하는 메서드입니다.
put
은 새로운 리소스를 생성하거나 이미 존재하는 데이터를 대체할 때 사용됩니다.
post
는 여러번 호출할 경우, 새로운 데이터가 지속적으로 추가됩니다.
반면, put
은 한 번 요청을 하거나 여러번 지속적으로 요청해도 결과값이 동일합니다.
예를 들어, 유저의 이름을 Batman
으로 수정하기 위해 axios.put
요청을 보냅니다.
이때, put
요청을 한 번 보내거나 여러번 보내도 유저의 이름은 Batman
으로 동일하게 수정됩니다.
특정 데이터 값을 삭제할 때 요청하는 메서드입니다.
callback
은 프로그래밍에서 빼놓을 수 없는 아주 중요한 개념입니다.
callback의 의미는 크게 2가지가 있습니다.
1. 다른 함수의 인자로 이용되는 함수
2. 이벤트에 의해 호출되어지는 함수
function add(x, y)() {
return x + y;
}
위에 add
함수에서는 인자로 x
, y
가 들어갑니다.
보통 문자열, 숫자열, 배열, 객체 등의 데이터를 인자로 많이 넣어봤습니다.
하지만 인자에는 함수도 들어갈 수 있습니다.
한 번 예제를 통해 보도록 하겠습니다.
function add(x, y, callback) {
let result = x + y;
callback(result);
}
function result (data) {
console.log(data, '콜백 함수 실행');
}
add(5, 10, result);
add
함수에는 인자로 x, y, callback
이 들어가고, callback에는 함수가 들어갑니다.
그리고 add
함수를 실행시키면 add 함수 안에서 인자로 들어간 다른 함수가 실행됩니다.
이럴 때 add 함수의 인자로 사용된 result 함수를 callback 함수라고 부릅니다.
이벤트라도 하면 어떤 보통 onClick, onChange
등이 떠오릅니다.
onClick, onChange
는 html에서 미리 만들어 놓은 함수입니다.
보통 onCLick
을 아래처럼 사용합니다.
<button onClick={handleClickButton}></button>
위의 모습을 보면 button을 클릭하면 onClick 함수가 실행되고, onCLick에서는 다시 handleClickFunction을 실행하게 됩니다.
위에서 얘기했듯이 함수의 인자로 handleClickFunction이 들어가기 떄문에 handleClickFunction을 callback 함수라고 부를 수 있습니다.
이런 상황에서 우리는 handleClickFunction을 호출한다, 호출된디라고도 합니다.
이러한 callback 함수는 2가지로 다시 나눌 수 있습니다.
1. 동기적 함수
2. 비동기적 함수
동기(synchronous)적 방식 : 현재 실행 중인 코드가 완료된 후 다음 코드를 실행
비동기(asynchronous)적 방식 : 현재 실행 중인 코드의 완료 여부와 무관하게 즉시 다음 코드로 넘어가서 실행
자바스크립트는 Single-Thread / Non-Blocking 방식으로 코드를 실행
합니다.
간단하게 이야기 하면, 자바스크립트는 한 번에 하나의 코드만 실행할 수 있습니다. (Single-Thread)
하지만 코드를 실행하고 해당 결과를 기다리지 않고 다음 코드를 실행합니다. (Non-Blocking)
비동기적 callback 함수의 가장 좋은 예시는 setTimeout
입니다.
비동기적 함수는 결과를 기다리지 않고 다음 코드를 실행하는 과정을 말합니다.
function Test() {
console.log('3초 기다리기');
}
setTimeout(Test, 3000);
console.log('이건 바로 실행');
위의 코드의 결과를 콘손을 통해 확인해보겠습니다.
'이건 바로 실행'
'3초 기다리기'
분명 setTimeout
을 먼저 실행시켰지만 하단에 있는 콘솔이 먼저 결과창에 보입니다.
이러한 이유는 자바스크립트 코드가 비동기적으로 코드를 실행시키기 떄문입니다.
위처럼 계속 비동기적 callback을 연속으로 사용하게 되는 경우에는 callback 지옥에 빠지게 됩니다.
콜백 지옥(callback hell)
은 콜백 함수를 익명 함수로 전달하는 과정이 반복되어 코드의 들여쓰기 수준이 감당하기 힘들정도로 깊어지는 현상을 얘기합니다.
주로 이벤트 처리나 서버 통신과 같은 비동기적인 작업을 수행하기 위해 이런 형태가 자주 등장하는데, 가독성이 떨어지면서 코드를 수정하기 어렵습니다.
예시를 보면서 콜백 지옥이 어떻게 작동하는지 확인해보겠습니다.
setTimeout((name) => {
let coffeeList = name;
console.log(coffeeList);
setTimeout((name) => {
coffeeList += ', ' + name;
console.log(coffeeList);
setTimeout((name) => {
coffeeList += ', ' + name;
console.log(coffeeList);
setTimeout((name) => {
coffeeList += ', ' + name;
console.log(coffeeList);
}, 500, 'Latte');
}, 500, 'Mocha');
}, 500, 'Americano');
}, 500, 'Expresso');
위 코드는 0.5초마다 커피 목록을 수집하고 출력합니다.
> 출력값
Expresso (0.5초)
Expresso, Americano (1.0초)
Expresso, Americano, Mocha (1.5초)
Expresso, Americano, Mocha, Latte (2.0초)
각 콜백의 커피 이름을 전달하고 목록에 이름을 추가합니다.
정상적으로 작동되지만 들여쓰기 수준이 과도하기 깊어지고 값이 아래에서 위로 전달되어 가독성이 떨어집니다.
이러한 것을 해결하기 위해 자바스크립트는 ES6의 Promise
를 추가했습니다.
axios는 기본적으로 Promise
를 지원하는 라이브러리입니다.
이번에는 Promise
와 axios를 이용해 Get 요청을 보내 data를 받아와보겠습니다.
var url = 'https://koreanjson.com/posts/1';
function getData() {
return new Promise(function(resolve, reject) {
axios.get(url).then(function(response) {
if(response) {
resolve(response.data);
}
reject(new Error('Request is failed'));
});
});
}
let result = [];
getData().then(function(data) {
for(let v of Object.values(data)) {
result.push(v);
}
console.log(result);
}).catch(function(err) {
console.log(err);
})
new 연산자와 함께 호출한 Promise
의 인자로 넘겨주는 콜백 함수는 호출할 때 바로 실행되지만 그 내부에 resolve
또는 reject
함수를 호출하는 구문이 있을 경우 둘 중 하나가 실행되기전까지는 then
또는 catch
로 넘어가지 않습니다.
따라서 비동기 작업이 완료될 때 resolve
또는 reject
를 호출하는 방법으로 비동기 작업의 동기적 표현이 가능해집니다.
resolve
는 보통 성공했을 때 반환값을 의미하고 reject
는 보통 실패했을 경우를 나타냅니다.
axios는 기본적으로 async/await
를 지원하는 라이브러리입니다.
이번에는 async/await
와 axios를 이용해 GET요청을 보내 data를 받아와보겠습니다.
async function getData(url) {
let data = (await axios.get(url)).data;
return data;
}
let url = 'https://koreanjson.com/posts/1';
async function dataArr() {
let data = await.getData(url);
let result = [];
for(let v of Object.values(data)) {
result.push(v);
}
console.log(result);
}
dataArr();
위 코드는 ES2017에 추가된 async/await
를 이용한 코드입니다.
비동기 작업을 수행하고자 하는 함수 앞에 async
를 표기하고, 함수 내부에서 실질적인 비동기 작업 필요한 위치마다 await
를 표기하는 것만으로 뒤의 내용을 Promise
로 자동 전환하고 해당 내용이 resolve
된 이후에야 다음으로 진행됩니다.
await
의 유무로 동기적 함수와 비동기적 함수를 구분할 수 있습니다.
06-01-async-await
폴더를 만들고, 안에 index.js
파일을 만들어주세요.
터미널에서 해당 폴더로 이동한 후, yarn init
또는 npm init
을 통해 package.json
파일을 만들어줍니다.
만들어진 package.json
파일에 "type": "module"
한 줄을 추가해줍니다.
{
"name": "06-01-async-await",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"type": "module"
}
axios를 설치하기 위해 터미널에서 yarn add axios
또는 npm install axios
를 입력해주어 패키지를 설치해줍니다.
이제 index.js
파일로 들어가 axios를 import
해줍니다.
import axios from "axios";
function fetchPost() {
}
fetchPost();
fetchPost
함수 안에 await을 쓰기 위해서 함수 앞에 async를 붙여주고, axios를 이용해 데이터를 받아오고, 그 결과를 출력하는 코드를 작성해줍니다.
import axios from "axios";
async function fetchPost() {
const result = await axios.get('https://koreanjson.com/posts/1');
console.log(result);
}
fetchPost();
터미널에서 node index.js
를 통해 파일을 실행해 주세요.
그러면 아래와 같은 결과가 출력되는 것을 확인할 수 있습니다.
이전에는 핸드폰 번호를 받아 토큰을 생성해 콘솔에서 확인해보는 부분까지 진행했었습니다.
이번에는 외부 API를 이용해 문자를 전송해보겠습니다.
API 사용 방법을 확인하기 위해 아래 사티으에서 Node.js SDK를 살펴보겠습니다.
Coolsms 사이트에 로그인이 되어야만 확인이 가능합니다.
https://console.coolsms.co.kr/oauth2/login
💡 SDK?
Software Development Kit의 약자로 개발자가 앱을 커스텀할 수 있도록 제공해주는 일종의 도구 모음과 같은 개념입니다.
Node.js SDK
를 살펴보면 아래와 같이 단문 문자 전송을 위한 예지를 확인할 수 있습니다.
이제 실제로 문자를 보내보겠습니다.
04-04-rest-api-with-express-3-token
폴더를 복사하여 사본을 만든 후 폴더 이름을 06-02-rest-api-with-sms
으로 변경해줍니다.
그 안에 index.js
파일을 생성해주고, 04-04-rest-api-with-express-3-token
폴더에서 phone.js
파일을 복사해옵니다.
그리고 아래의 명령어를 통해 모듈을 설치해줍니다.
npm install coolsms-node-sdk
or
yarn add coolsms-node-sdk
phone.js
로 가서 coolsms-node-sdk
를 import
해줍니다.
import coolsms from 'coolsms-node-sdk'
이제 sendTokenToSMS
함수를 수정하겠습니다.
아래와 같이 내용을 입력하여 수정해주세요.
동시에 import한 coolsms에서 SDK
를 사용할 수 있도록 데이터를 가져오는 코드를 추가합니다.
export function sendTokenToSMS(phone, token) {
const SMS_KEY = '본인의 Coolsms API Key'
const SMS_SECRET = '본인의 Coolsms API Secret'
const SMS_SENDER = '발신번호로 등록했던 본인의 휴대폰 번호'
const mySms = coolsms.default; // SDK 가져오기
console.log(phone + '번호로 인증번호' + token + '을 전송합니다.');
}
SDK 예시를 참고해 요청을 보내는 코드를 작성합니다.
여기서는 비동기 작업를 진행하기때문에 async/await
를 사용해야합니다.
export async function sendTokenToSMS(phone, token) {
const SMS_KEY = '본인의 Coolsms API Key'
const SMS_SECRET = '본인의 Coolsms API Secret'
const SMS_SENDER = '발신번호로 등록했던 본인의 휴대폰 번호'
const mySms = coolsms.default; // SDK 가져오기
const messageService = new mySms(SMS_KEY, SMS_SECRET);
const result = await messageService.sendOne({
to: phone,
from: SMS_SENDER,
text: `안녕하세요! 요청하신 인증번호는 ${token}입니다.`,
})
console.log(phone + '번호로 인증번호' + token + '을 전송합니다.');
}
앞서 만들었던 sendTokenToSMS
함수를 수정해보겠습니다.
소스코드에서 바로 사용하였던 중요한 키값들을 변수로 꺼내어 따로 값을 할당해 줍니다.
06-02-rest-api-with-sms
폴더에서 .env
파일을 만들어 줍니다.
파일에서 새로운 변수를 선언하고 본인의 APIKey, SecretKey
등의 값을 할당해 줍니다.
여기서 값을 할당할 때 따옴표는 생략합니다.
SMS_KEY = 본인의 Coolsms API Key
SMS_SECRET = 본인의 Coolsms API Secret
SMS_SENDER = 발신번호로 등록했던 본인의 휴대폰 번호
이제 .env
파일에서 설정한 값들이 sendTokenToSMS
함수에 적용될 수 있게 코드를 수정합니다.
export async function sendTokenToSMS(phone, token) {
const SMS_KEY = process.env.SMS_KEY; // 본인의 Coolsms API Key 입력
const SMS_SECRET = process.env.SMS_SECRET; // 본인의 Coolsms API Secret 입력
const SMS_SENDER = process.env.SMS_SENDER;
const mySms = coolsms.default; // SDK 가져오기
const messageService = new mySms(SMS_KEY, SMS_SECRET);
const result = await messageService.sendOne({
to: phone,
from: SMS_SENDER,
text: `안녕하세요! 요청하신 인증번호는 ${token}입니다.`,
})
console.log(phone + '번호로 인증번호' + token + '을 전송합니다.');
}
하지만 종종 환경변수 파일을 읽어오지못해 process.env...
에서 에러가 발생할 수도 있습니다.
파일을 읽어올 수 있게 라이브러리 하나를 설치해주겠습니다.
npm install dotenv
or
yarn add dotenv
그리고 phone.js
에 import
해줍니다.
import 'dotenv/config'
이제 process.env
라는 명령어를 사용하여 변수를 선언하게 되면 해당 명령어가 .env
의 파일의 키값을 찾아 읽어줍니다.
그리고 .gitignore
파일에 .env
를 추가하여 소스코드가 공유될 수 없도록 설정해줍니다.
이제 터미널에서 해당 폴더로 이동하여 서버를 띄워주고 postman으로 문자를 받을 핸드폰 번호를 입력하고 Send를 눌러 요청합니다.
터미널에 콘솔이 잘 찍히고, 문자도 정상적으로 받았습니다.
이번에는 이메일을 실제로 전송해보겠습니다.
Nodemailer의 공식 문서를 참고해 주세요.
06-02-rest-api-with-sms
폴더를 복사하여 사본을 만들어주고 폴더명을 06-03-rest-api-with-sms-email
로 변경해줍니다.
아래의 명령어를 입력해 Nodemailer를 설치해주세요.
npm install nodemailer
or
yarn add nodemailer
02-06-welcome-template-destructuring-api-date
폴더 안에 있는 date.js
파일과 email.js
파일을 복사해 가져옵니다.
// date.js 파일
export function getCreatedAt() {
const date = new Date();
const yyyy = date.getFullYear();
const mm = date.getMonth() + 1;
const dd = date.getDate();
return `${yyyy}-${mm}-${dd}`;
}
email.js
에 코드를 조금 수정해주겠습니다.
// email.js 파일
import { getCreatedAt } from './date.js'
// 이메일 정규식
const email_reg = /^[A-Za-z0-9_\.\-]+@[A-Za-z0-9\-]+\.[A-Za-z0-9\-]+/;
export function checkValidateEmail(email) {
// 이메일 유효성 검사를 하여 통과되면 true를 반환
if(email_reg.test(email) === false) {
// 에러 발생을 알려준다.
console.log('에러발생! 이메일을 제대로 입력해 주세요.');
return false;
}
return true;
}
export function getWelcomeTemplate({name, age, school}) {
const createdAt = getCreatedAt();
return`
<html>
<body>
<h1>${name}님 가입을 환영합니다.</h1>
<hr/>
<span>이름 : ${name}</span>
<span>나이 : ${age}</span>
<span>학교 : ${school}</span>
<span>가입일 : ${createdAt}</span>
</body>
</html>
`
}
export function sendWelcomeTemplateToEmail(email, template) {
// 테플릿을 이메일에 전송
console.log(`${email}로 템플릿 ${template}을 전송합니다.`)
}
가입 환영 이메일을 보내는 API를 index.js
파일에 추가해보겠습니다.
import { checkValidateEmail, getWelcomeTemplate, sendWelcomeTemplateToEmail } from './email.js'
// ... 기존 코드
app.post('/users', (req, res) => {
const user = req.body.myuser;
// 1. 이메일이 정상인지 확인한다(1-존재여부, 2-'@'포함여부)
const isValid = checkValidateEmail(user.email);
if(isValid) {
// 2. 가입 환영 템플릿 만들기
const myTemplate = getWelcomeTemplate(user);
// 3. 이메일에 가입 환영 템플릿 추가하기
sendWelcomeTemplateToEmail(user.email, myTemplate);
res.send('가입완료!')
}
})
app.listen(3000, () => {
console.log(`Example app listening on port ${3000}`)
});
이제 email.js
파일로 가서 설치한 Nodemailer를 import
하고 메일을 전송하는 코드를 작성하겠습니다.
마찬가지로 비동기 작업을 수행하니 async/await
를 사용합니다.
import nodemailer from 'nodemailer';
// ... 기존 코드
export async function sendWelcomeTemplateToEmail(email, template) {
const EMAIL_USER = "구글 계정 2단계 인증을 설정한 본인 이메일";
const EMAIL_PASS = "구글 계정 관리에서 발급 받았던 본인의 앱 비밀번호";
const EMAIL_SENDER = "발신자 이메일";
const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: EMAIL_USER,
pass: EMAIL_PASS
}
})
const result = await transporter.sendMail({
from: EMAIL_SENDER,
to: email,
subject: '가입을 축하합니다!!!',
html: template
});
console.log(result);
console.log(`${email}로 템플릿 ${template}을 전송합니다.`)
}
이제 해당 폴더로 이동해 서버를 실행해주세요.
postman을 열어서 http://localhost:3000/users
으로 POST 요청을 보내겠습니다.
Body
부분에 JSON 형식으로 아래와 같이 user
정보를 적어주세요.
email
에는 이메일을 받을 주소를 입력해주세요.
메일이 정상적으로 수신된 것을 확인할 수 있습니다.
SMS 전송 때와 마찬가지로 .env
파일에 이메일 전송 관련 환경 변수를 관리해주겠습니다.
EMAIL_PASS = 구글 계정 관리에서 발급 받았던 본인의 앱 비밀번호
EMAIL_SENDER = 구글 계정 2단계 인증을 설정한 본인 이메일
EMAIL_USER = 구글 계정 2단계 인증을 설정한 본인 이메일
그리고 환경변수가 잘 적용되도록 sendWelcomeTemplateEmail
함수의 코드도 수정해줍니다.
import 'dotenv/config.js'
// ... 기존 코드
export async function sendWelcomeTemplateToEmail(email, template) {
const EMAIL_USER = process.env.EMAIL_USER; // 구글 계정 2단계 인증을 설정한 본인 이메일
const EMAIL_PASS = process.env.EMAIL_PASS; // 구글 계정 관리에서 발급 받았던 본인의 앱 비밀번호
const EMAIL_SENDER = process.env.EMAIL_SENDER;
const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: EMAIL_USER,
pass: EMAIL_PASS
}
})
const result = await transporter.sendMail({
from: EMAIL_SENDER,
to: email,
subject: '가입을 축하합니다!!!',
html: template
});
console.log(result);
console.log(`${email}로 템플릿 ${template}을 전송합니다.`)
}