Node.js에서 api를 작성하는 방법을 예제를 통해 정리한다.
api는 기본적으로 요청/응답한다.
다음은 GET 메소드를 통한 요청/응답하는 api 이며,
2가지 경로로 요청한 경우를 처리하는 예제이다.
예를들면 다음과 같은 경우이다.
1. http://127.0.0.1:3000/
2. http://127.0.0.1:3000/data?qs1=2&qs2=3
example.js
"use strict";
const http = require('http');
const url = require('url'); // url문자열 or 객체값을 쉽게 가져올 수 있음
const hostname = '127.0.0.1';
const port = 3000;
const server = http.createServer((req, res) => {
switch (req.method) {
case 'GET':
if (req.url === '/') {
res.setHeader('Content-Type', 'text/plain');
res.writeHead(200);
res.end('Hello! Node.js HTTP Server');
} else if (req.url.substring(0, 5) === '/data') {
const queryParams = url.parse(req.url, true).query;
res.setHeader('Content-Type', 'text/html');
res.writeHead(200);
res.write('<html><head><title>JavaScript 200제</title></head>');
for (let key in queryParams) {
res.write(`<h1>${key}</h1>`);
res.write(`<h2>${queryParams[key]}</h2>`);
}
res.end('</body></html>');
}
break;
default: // case에 없는 경우 바로 완료 처리
res.end();
}
});
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
http의 createServer()메소드를 통해 서버를 생성한다.
req의 method를 통해 4가지중 어떤 동작을 하는지 파악할 수 있다.(GET, POST, PUT, DELETE)
case 'GET'을 작성해 GET으로 요청한 경우를 처리해준다.
req의 url을 통해 요청한 경로가 '/'를 포함하는 요청인 경우
(http://127.0.0.1:3000/ 이런식인 경우)
기본적인 헤더값과 상태요청코드(200), res의 end()메소드를 통해 'Hello! Node.js HTTP Server'문구를 전달하고 끝낸다.
req의 url을 통해 요청한 경로가 '/data'를 포함하는 요청인 경우
(http://127.0.0.1:3000/data?qs1=2&qs2=3 이런식인 경우)
url의 parse()메소드를 통해 url의 문자열값을 객체값으로 파싱한다.
(파싱한 객체값은 이런식으로 들어가 있다. queryParams: {qs1: '1', qs2: '2'})
기본적인 헤더값과 상태요청코드(200), res의 write()메소드를 통해 html코드를 전달한다.
파싱한 객체값은 for-each문으로 key와 value를 접근하면서 res의 write()메소드로 html코드를 연달아 보내준다.
다 보냈으면 res의 end()메소드로 열어놓았던 html태그를 모두 닫아준 뒤 마무리한다.
(즉, write로 헤더정보 및 html코드 연달아 보내다가 end로 마무리한 것)
GET요청을 웹 브라우저의 url입력을 통해 호출하였지만 http.get()메소드를 통해서도 가능하다.
보통 http의 request()메소드를 사용하지만, body 데이터의 전송 없이 간단하게 요청할 때는
http.get()를 사용한다.
example.js
http.get('http://localhost:3000', (res) => {
let data = '';
res.on('data', function(chunk) {
data += chunk;
console.log('data of res.on =====> ', data);
});
res.on('end', function() {
try {
console.log('end of res.on =====> ', data);
return data;
} catch (err) {
if (err) console.log(err);
}
});
});
Node.js에서 fs, path 모듈을 사용하여 파일의 내용을 읽는 방법을 예제를 통해 정리한다.
example.js
"use strict";
const fs = require('fs');
const path = require('path');
const filePath = path.join(__dirname, 'js200', 'hello.txt');
fs.open(filePath, 'r', (err, fd) => { // A
if (err && err.code === 'ENOENT') return console.log('읽을 수 없는 파일입니다');
if (err) return console.log(err);
fs.readFile(filePath, 'utf-8', (err, data) => { // B
if (err) return console.log(err);
console.log(data);
});
try { // C
const data = fs.readFileSync(filePath, 'utf-8');
console.log(data);
} catch (err) {
console.log(err);
}
});
위에 주석친 부분을 문단단위로 정리한다.
[A] fs.open()메소드를 통해 해당 경로의 파일을 'r'모드로 open한다.
(읽을 수 없거나 기타 에러시 에러정보를 출력한다)
[B] fs의 readFile()메소드를 통해 파일을 읽어온다.
[fs.readFile(파일경로, 인코딩, 콜백함수)]
그 후, 파일정보를 출력한다.
이 방식은 비동기 패턴이다.
[C] fs의 readFileSync()메소드를 통해 반환된 파일정보를 출력한다.
[fs.readFileSync()메소드 파일경로, 인코딩]
Sync가 붙은 이유는 동기패턴이기 때문이다.
동기 패턴은 에러에 대한 값이 반환되는 것은 아니기 때문에
try-catch문을 통해 에러를 catch해야 한다.
그렇지 않고 동기 패턴에서 에러가 발생하면 idle상태에 빠져서 작동하지 않게 된다.
Node.js에서 fs, path 모듈을 사용하여 파일을 삭제하는 방법을 예제를 통해 정리한다.
example.js
'use strict';
const fs = require('fs');
const path = require('path');
const filePath = path.join(__dirname, 'js200', 'hello.txt');
fs.access(filePath, fs.constants.F_OK, (err) => { // A
if (err) return console.log('삭제할 수 없는 파일입니다');
fs.unlink(filePath, (err) => err ?
console.log(err) : console.log(`${filePath} 를 정상적으로 삭제했습니다`));
});
위에 주석친 부분을 문단단위로 정리한다.
[A] fs의 access()메소드로 해당경로에 대한 접근가능여부 확인한다.
(에러발생 시 에러문구를 출력한다.)
[fs.access(경로, mode정보, 콜백함수)]
(fs.constants.F_OK = 접근과 관련된 mode 정보)
(constants = 파일 시스템과 관련된 상수들을 그룹으로 모아놓은 상수.)
(그 중 상수 F_OK = 파일 존재 여부 확인)
fs.constants.F_OK를 전달하고 에러를 반환하지 않은 경우
MODE 상수값 조건을 충족(삭제할 수 있는)한다는 뜻이므로
fs.unlink() 메소드를 이용해 삭제한다.
F_OK말고도 다양한 상수가 있다.
O_RDONLY : read-only 접근을 위해 파일을 여는 권한
X_OK : 파일이 다른 프로세스 호출에 의해 실행 가능한지 접근 권한을 확인
S_IWUSR, S_IXGRP : 파일 쓰기에서 소유권 또는 그룹소유권 확인
더 자세한 내용은 아래 링크에서 확인할 수 있다.
https://nodejs.org/docs/latest/api/fs.html#fs_fs_constants_1
Node.js에서 fs, path 모듈을 사용하여 파일 및 폴더를 삭제하는 방법을 예제를 통해 정리한다.
example.js
'use strict';
const fs = require('fs');
const path = require('path');
const removePath = (p, callback) => { // A
fs.stat(p, (err, stats) => {
if (err) return callback(err);
if (!stats.isDirectory()) { // B
console.log('이 경로는 파일');
return fs.unlink(p, err => err ? callback(err) : callback(null, p));
}
console.log('이 경로는 폴더'); // C
fs.rmdir(p, (err) => {
if (err) return callback(err);
return callback(null, p);
});
});
};
const printResult = (err, result) => {
if (err) return console.log(err);
console.log(`${result} 를 정상적으로 삭제했습니다`);
};
const p = path.join(__dirname, 'js200');
try { // D
const files = fs.readdirSync(p);
if (files.length)
files.forEach(f => removePath(path.join(p, f), printResult));
} catch (err) {
if (err) return console.log(err);
}
removePath(p, printResult);
특정 경로의 폴더안에 있는 파일들을 모두 삭제한 후, 마지막에 폴더까지 삭제한다.
위에 주석친 부분을 문단단위로 정리한다.
[A] removePath
특정 경로의 파일 또는 폴더를 삭제하는 함수이다.
fs.stat()메소드를 통해 해당 경로가 파일인지 폴더인지 확인한다.
(에러발생 시 에러정보를 출력한다)
[B] stats.isDirectory()메소드가 false일 경우.
즉, 파일인 경우(폴더가 아닌 경우)
fs.unlink()메소드를 통해 해당 경로의 파일을 삭제한다.
[C] 폴더인 경우(파일이 아닌 경우)
fs.rmdir()메소드를 통해 해당 경로의 폴더를 삭제한다.
[D] 동기패턴인 readdirSync()메소드를 통해 해당경로(p)의 폴더의 파일들을 가져온다.
가져온 파일의 개수(length)가 0이 아니면(0이면 빈 폴더라는 것)
forEach로 하나하나 돌면서 removePath()메소드를 통해 삭제한다.
(해당경로(p)의 파일명 값(f)을 접근해서 삭제한다)
폴더 안 파일들을 모두 삭제했으면 해당경로(p)의 폴더 자체를 삭제해준다.
Node.js에서 fs, path 모듈을 사용하여 파일을 검색하는 방법을 예제를 통해 정리한다.
example.js
"use strict";
const fs = require('fs');
const path = require('path');
const fileName = 'hello.txt'; // A
const filePath = path.join(__dirname, 'js200', fileName);
console.log(path.parse(filePath));
const isFileInPath = (path, fileName, callback) => { // B
fs.readdir(path, (err, files) => {
if (err) return callback(err);
let isHere = false;
files.forEach(f => {
if (f === fileName) isHere = true;
});
return callback(null, isHere);
});
};
isFileInPath(path.join(__dirname, 'js200'), fileName, (err, isTrue) => { // C
if (err || !isTrue) return console.log('파일을 읽을 수 없습니다');
fs.stat(filePath, (err, fileInfo) => {
if (err) return console.log(err);
return console.log(fileInfo);
});
});
위에 주석친 부분을 문단단위로 정리한다.
[A] 파일 경로를 생성한다.
[A] isFileInPath 메소드
isFileInPath(경로, 파일명, 콜백함수)
해당경로에 파일이 존재하는지 파악하는 하는 함수.
fs의 readdir함수를 통해 특정 경로 안에 있는 모든 파일명을 콜백 함수의 매개변수로 전달하고
모든 파일을 forEach로 하나씩 꺼내서 동일한 파일명이 존재할 경우 true를 반환한다.
[B] 위에서 정의한 isFileInPath 메소드를 실행한다.
false를 반환했을 경우(동일한 파일명이 존재하지 않음)나
기타에러가 발생한 경우 '파일을 읽을 수 없습니다' 출력 후 종료한다.
그렇지 않은 경우 fs.stat함수를 통해 파일의 상세정보(fileInfo)를 출력한다.
Node.js에서 fs, path 모듈을 사용하여 파일에 입력하는 방법을 예제를 통해 정리한다.
example.js
"use strict";
const fs = require('fs');
const path = require('path');
const makeFile = (path, callback) => { // A
fs.writeFile(path, 'New file, New content', 'utf8', (err) => {
if (err) return callback(err);
console.log('파일이 생성됐습니다.');
callback(null);
});
};
const appendFile = (path, callback) => { // B
fs.appendFile(path, '\nUpdate file', (err) => {
if (err) return callback(err);
console.log('파일 내용을 추가합니다.');
callback(null);
})
};
const printErrIfExist = (err) => { // C
if (err) console.log(err);
};
const folderName = 'test' // D
const fileName = 'hello.txt'
const filePath = path.join(__dirname, folderName, fileName);
fs.open(filePath, 'wx', (err, fd) => { // E
if (err && err.code === 'EEXIST')
return appendFile(filePath, (err) => printErrIfExist(err));
if (err) {
return callback(err);
}
return makeFile(filePath, (err) => printErrIfExist(err));
});
위에 주석친 부분을 문단단위로 정리한다.
[A] makeFile
새로운 파일에 내용을 추가하는 하는 함수.
fs모듈의 writeFile()메소드에 경로, 파일내용, 인코딩, 에러콜백함수를 전달해 파일을 새로 생성한다.
[B] appendFile
기존파일에 내용을 추가하는 함수
fs의 appendFile()메소드를 이용해 경로, 추가할 문자열, 에러콜백함수를 전달해 기존파일에 내용을 추가한다.
[C] printErrIfExist
에러가 존재할 시 에러를 출력하는 함수
[D] path의 join모듈로 경로를 만들어준다.
// path.join(현재 경로, 폴더명, 파일명)
[E] fs.open()
해당경로의 파일 또는 폴더의 존재 여부 확인 함수
fs.open(경로, wx(쓰기 접근 권한), 에러정보)
동일한 파일이 있을 경우. appendFile함수로 내용만 추가한다.
그렇지 않을 경우. makeFile함수로 새로운 파일에 내용을 추가한다.
그 외 기타 오류가 발생한 경우 에러를 반환한다.
파일을 생성할 때 해당 경로에 폴더가 존재하지 않으면
fs.open을 실행할 때 에러가 발생한다.
그래서 미리 만들어놔야 한다.
아니면 폴더가 없는 경우 새로 생성해주는 방법도 있을 것 같은데
폴더가 하나만 없다면 생성해주면 되지만 여러 개가 없을 경우도 존재할 것이다.
이 부분은 구현하려다 코드만 괜히 더러워질 것 같아서 말았다.