728x90
수업내용
1교시 (09:30-10:20)
- JavaScript, React 복습
2교시 (10:30-11:20)
- 02_elements.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Document</title>
</head>
<body>
<!-- Target Contrainer -->
<div id="react-container"></div>
<!-- React Library & React DOM -->
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script>
// const dish = React.createElement("h1", null, "구운 연어")
const dessert = React.createElement("h2", null, "코코넛 크림 파이");
const dish = React.createElement(
"h1",
{id: "recipe-0", 'data-type': "title"},
"구운 연어"
)
ReactDOM.render(
[dish, dessert],
document.getElementById('react-container')
)
console.log('dish', dish);
</script>
</body>
</html>
- 03_elements.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- Target Container -->
<div id="react-container"></div>
<!-- React Library & React DOM-->
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script>
const dish = React.createElement("section", {id: "baked-salmon"},
React.createElement("h1", null, "구운 연어"),
React.createElement("ul", {"className": "ingredients"},
React.createElement("li", null, "연어 500그램"),
React.createElement("li", null, "잣 1 컵"),
React.createElement("li", null, "버터 상추 2 컵"),
React.createElement("li", null, "옐로 스쿼시(Yellow Squash, 호박의 한 종류) 1개"),
React.createElement("li", null, "올리브 오일 1/2 컵"),
React.createElement("li", null, "마늘 3 쪽")
),
React.createElement("section", {"className": "instructions"},
React.createElement("h2", null, "조리절차"),
React.createElement("p", null, "오븐을 350도로 예열한다."),
React.createElement("p", null, "유리 베이킹 그릇에 올리브 오일을 두른다."),
React.createElement("p", null, "연어, 마늘, 잣을 그릇에 담는다."),
React.createElement("p", null, "오븐에서 15분간 익힌다."),
React.createElement("p", null, "옐로 스쿼시를 추가하고 다시 30분간 오븐에서 익힌다."),
React.createElement("p", null, "오븐에서 그릇을 꺼내서 15분간 식힌다음에 상추를 곁들여서 내놓는다.")
)
)
ReactDOM.render(dish, document.getElementById('react-container'))
console.log('dish element', dish)
</script>
</body>
</html>
3교시 (11:30-12:20)
- photo.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h3>파일 업로드</h3>
<hr>
<form method="POST" enctype="multipart/form-data" action="/process/photo">
<table>
<tr>
<td><label>파일</label></td>
<td><input type="file" name="photo"></td>
</tr>
</table>
<input type="submit" value="업로드" name="submit">
</form>
</body>
</html>
- express_11.js
// Express 기본 모듈 불러오기
var express = require('express')
, http = require('http')
, path = require('path');
// Express의 미들웨어 불러오기
var bodyParser = require('body-parser')
, static = require('serve-static');
// 파일 처리
var gs = require('fs');
// 파일 업로드용 미들웨어
var multer = require('multer');
// 클라이언트에서 ajax로 요청 시 CORS(다중 서버 접속) 지원
var cors = require('cors');
// mime 모듈
var mime = require('mime');
// 익스프레스 객체 생성
var app = express();
// 기본 속성 설정
app.set('port', process.env.PORT || 3000);
// body-parser 설정
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
app.use('/public', static(path.join(__dirname, 'public')));
app.use('/uploads', static(path.join(__dirname, 'uploads')));
// 클라이언트에서 ajax로 요청 시 CORS(다중 서버 접속) 지원
app.use(cors());
// multer 미들웨어 사용 : 미들웨어 사용 순서 중요 body-parser -> multer -> router
// 파일 제한 : 10ro, 1G
var storage = multer.diskStorage({
destination: function(req, file, callback) {
callback(null, 'uploads');
},
filename: function(req, file, callback){
// callbac(null, file.originalname + Date.now());
var extension = path.extname(file.originalname);
var basename = path.basename(file.originalname, extension);
callback(null, basename, Date.now() + extension);
}
});
console.log('storage >>> : ' + storage);
var upload = multer({
storage: storage,
limits: {
files: 10,
fileSize: 1024 * 1024 * 1024
}
});
// 라우터 사용하여 라우팅 함수 등록
var router = express.Router();
router.route('/process/photo').post(upload.array('photo', 1), function(req, res) {
console.log('/process/photo 호출됨');
try {
var files = req.files;
console.log("files >>> : " + files);
console.dir('#=============== 업로드된 첫번째 파일 정보#');
console.dir(req.files[0]);
console.dir('#===============#');
// 현재의 파일 정보를 저장할 변수 선언
var originalname = ''
filename = '';
if (Array.isArray(files)) { // 배열에 들어가 있는 경우 (설정에서 1개의 파일도 배열에 넣게 했음)
console.log("배열에 들어있는 파일 갯수: %d", files.length);
for (var i=0; i < files.length; i++) {
originalname = files[i].originalname;
console.log("originalname >>> : " + originalname);
filename = files[i].filename;
console.log("filename >>> : " + filename);
}
} else {
console.log('업로드된 파일이 배열에 들어가 있지 않습니다.');
}
console.log('현재 파일 정보 : ' + originalname);
// 클라이언트에 응답 전송
// res.writeHead('200', {'Content-Type': 'text/html; charset=utf8'});
// res.write('<h1>파일 업로드 성공</h1>');
// res.write('<hr/>');
// res.write('<p>원본 파일명 : ' + originalname + ' </p>');
// res.end();
res.writeHead(200, {'Content-Type': 'text/html; charset=utf8'});
res.write('<h1>파일 업로드 성공</h1>');
res.write('<img src="/uploads/' + filename + '" width="200px">');
res.write('<div><input type="button" value="다시 작성" onclick="javascript:history.back()"></div>');
res.end();
} catch (err) {
console.dir(err.stack);
}
});
app.use('/', router);
// Express 서버 시작
http.createServer(app).listen(app.get('port'), function(){
console.log('Express server listening on port ' + app.get('port'));
});
4교시 (12:30-13:20)
- login2.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>로그인 테스트</title>
</head>
<body>
<h3>로그인</h3>
<hr>
<form method="POST" action="/process/login">
<table>
<tr>
<td><label>아이디</label></td>
<td><input type="text" name="id"></td>
</tr>
<tr>
<td><label>비밀번호</label></td>
<td><input type="text" name="password"></td>
</tr>
</table>
<input type="submit" value="전송" name="">
</form>
</body>
</html>
- adduser2.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h3>MySQL 사용자추가</h3>
<hr>
<form method="post" action="/process/adduser">
<table>
<tr>
<td><label>아이디</label></td>
<td><input type="text" name="id" ></td>
</tr>
<tr>
<td><label>사용자명</label></td>
<td><input type="text" name="name" ></td>
</tr>
<tr>
<td><label>나이</label></td>
<td><input type="text" name="age" ></td>
</tr>
<tr>
<td><label>비밀번호</label></td>
<td><input type="password" name="password" ></td>
</tr>
</table>
<input type="submit" value="전송" >
</form>
</body>
</html>
- mysql_01.js
/**
* MySQL 데이터베이스 사용하기
*
* 웹브라우저에서 아래 주소의 페이지를 열고 웹페이지에서 요청
* (먼저 사용자 추가 후 로그인해야 함)
* http://localhost:3000/public/login2.html
* http://localhost:3000/public/adduser2.html
*
*
* npm i mysql --save
*
*/
/*
root/1234
C:\Program Files\MySQL\MySQL Server 8.0\bin>mysql -u root -p
Enter password: ****
mysql> ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '1234';
Query OK, 0 rows affected, 1 warning (0.00 sec)
*/
// Express 기본 모듈 불러오기
var express = require('express')
, http = require('http')
, path = require('path');
// Express의 미들웨어 불러오기
var bodyParser = require('body-parser')
, static = require('serve-static');
//===== MySQL 데이터베이스를 사용할 수 있도록 하는 mysql 모듈 불러오기 =====//
var mysql = require('mysql');
//===== MySQL 데이터베이스 연결 설정 =====//
var pool = mysql.createPool({
connectionLimit : 10,
host : 'localhost',
user : 'root',
password : '1234',
database : 'test',
debug : false
});
// 익스프레스 객체 생성
var app = express();
// 설정 파일에 들어있는 port 정보 사용하여 포트 설정
app.set('port', process.env.PORT || 3000);
// body-parser를 이용해 application/x-www-form-urlencoded 파싱
app.use(bodyParser.urlencoded({ extended: false }))
// body-parser를 이용해 application/json 파싱
app.use(bodyParser.json())
// public 폴더를 static으로 오픈
app.use('/public', static(path.join(__dirname, 'public')));
//===== 라우팅 함수 등록 =====//
// 라우터 객체 참조
var router = express.Router();
// 로그인 처리 함수
router.route('/process/login').post(function(req, res) {
console.log('/process/login 호출됨.');
// 요청 파라미터 확인
var paramId = req.body.id || req.query.id;
var paramPassword = req.body.password || req.query.password;
console.log('요청 파라미터 : ' + paramId + ', ' + paramPassword);
// pool 객체가 초기화된 경우, authUser 함수 호출하여 사용자 인증
if (pool) {
authUser(paramId, paramPassword, function(err, rows) {
// 에러 발생 시, 클라이언트로 에러 전송
if (err) {
console.error('사용자 로그인 중 에러 발생 : ' + err.stack);
res.writeHead('200', {'Content-Type':'text/html;charset=utf8'});
res.write('<h2>사용자 로그인 중 에러 발생</h2>');
res.write('<p>' + err.stack + '</p>');
res.end();
return;
}
// 조회된 레코드가 있으면 성공 응답 전송
if (rows) {
console.dir(rows);
// 조회 결과에서 사용자 이름 확인
var username = rows[0].name;
res.writeHead('200', {'Content-Type':'text/html;charset=utf8'});
res.write('<h1>로그인 성공</h1>');
res.write('<div><p>사용자 아이디 : ' + paramId + '</p></div>');
res.write('<div><p>사용자 이름 : ' + username + '</p></div>');
res.write("<br><br><a href='/public/login2.html'>다시 로그인하기</a>");
res.end();
} else { // 조회된 레코드가 없는 경우 실패 응답 전송
res.writeHead('200', {'Content-Type':'text/html;charset=utf8'});
res.write('<h1>로그인 실패</h1>');
res.write('<div><p>아이디와 패스워드를 다시 확인하십시오.</p></div>');
res.write("<br><br><a href='/public/login2.html'>다시 로그인하기</a>");
res.end();
}
});
} else { // 데이터베이스 객체가 초기화되지 않은 경우 실패 응답 전송
res.writeHead('200', {'Content-Type':'text/html;charset=utf8'});
res.write('<h2>데이터베이스 연결 실패</h2>');
res.write('<div><p>데이터베이스에 연결하지 못했습니다.</p></div>');
res.end();
}
});
// 사용자 추가 라우팅 함수
router.route('/process/adduser').post(function(req, res) {
console.log('/process/adduser 호출됨.');
var paramId = req.body.id || req.query.id;
var paramPassword = req.body.password || req.query.password;
var paramName = req.body.name || req.query.name;
var paramAge = req.body.age || req.query.age;
console.log('요청 파라미터 : ' + paramId + ', ' + paramPassword + ', ' + paramName + ', ' + paramAge);
// pool 객체가 초기화된 경우, addUser 함수 호출하여 사용자 추가
if (pool) {
addUser(paramId, paramName, paramAge, paramPassword, function(err, addedUser) {
// 동일한 id로 추가하려는 경우 에러 발생 - 클라이언트로 에러 전송
if (err) {
console.error('사용자 추가 중 에러 발생 : ' + err.stack);
res.writeHead('200', {'Content-Type':'text/html;charset=utf8'});
res.write('<h2>사용자 추가 중 에러 발생</h2>');
res.write('<p>' + err.stack + '</p>');
res.end();
return;
}
// 결과 객체 있으면 성공 응답 전송
if (addedUser) {
console.dir(addedUser);
console.log('inserted ' + result.affectedRows + ' rows');
var insertId = result.insertId;
console.log('추가한 레코드의 아이디 : ' + insertId);
res.writeHead('200', {'Content-Type':'text/html;charset=utf8'});
res.write('<h2>사용자 추가 성공</h2>');
res.end();
} else {
res.writeHead('200', {'Content-Type':'text/html;charset=utf8'});
res.write('<h2>사용자 추가 실패</h2>');
res.end();
}
});
} else { // 데이터베이스 객체가 초기화되지 않은 경우 실패 응답 전송
res.writeHead('200', {'Content-Type':'text/html;charset=utf8'});
res.write('<h2>데이터베이스 연결 실패</h2>');
res.end();
}
});
// 라우터 객체 등록
app.use('/', router);
// 사용자를 인증하는 함수
var authUser = function(id, password, callback) {
console.log('authUser 호출됨 : ' + id + ', ' + password);
// 커넥션 풀에서 연결 객체를 가져옴
pool.getConnection(function(err, conn) {
if (err) {
if (conn) {
conn.release(); // 반드시 해제해야 함
}
callback(err, null);
return;
}
console.log('데이터베이스 연결 스레드 아이디 : ' + conn.threadId);
var columns = ['id', 'name', 'age'];
var tablename = 'users';
// SQL 문을 실행합니다.
var exec = conn.query("select ?? from ?? where id = ? and password = ?", [columns, tablename, id, password], function(err, rows) {
conn.release(); // 반드시 해제해야 함
console.log('실행 대상 SQL : ' + exec.sql);
if (rows.length > 0) {
console.log('아이디 [%s], 패스워드 [%s] 가 일치하는 사용자 찾음.', id, password);
callback(null, rows);
} else {
console.log("일치하는 사용자를 찾지 못함.");
callback(null, null);
}
});
conn.on('error', function(err) {
console.log('데이터베이스 연결 시 에러 발생함.');
console.dir(err);
callback(err, null);
});
});
}
//사용자를 등록하는 함수
var addUser = function(id, name, age, password, callback) {
console.log('addUser 호출됨 : ' + id + ', ' + password + ', ' + name + ', ' + age);
// 커넥션 풀에서 연결 객체를 가져옴
pool.getConnection(function(err, conn) {
if (err) {
if (conn) {
conn.release(); // 반드시 해제해야 함
}
callback(err, null);
return;
}
console.log('데이터베이스 연결 스레드 아이디 : ' + conn.threadId);
// 데이터를 객체로 만듦
var data = {id:id, name:name, age:age, password:password};
// SQL 문을 실행함
var exec = conn.query('insert into users set ?', data, function(err, result) {
conn.release(); // 반드시 해제해야 함
console.log('실행 대상 SQL : ' + exec.sql);
if (err) {
console.log('SQL 실행 시 에러 발생함.');
console.dir(err);
callback(err, null);
return;
}
callback(null, result);
});
conn.on('error', function(err) {
console.log('데이터베이스 연결 시 에러 발생함.');
console.dir(err);
callback(err, null);
});
});
}
//===== 서버 시작 =====//
// 프로세스 종료 시에 데이터베이스 연결 해제
process.on('SIGTERM', function () {
console.log("프로세스가 종료됩니다.");
});
app.on('close', function () {
console.log("Express 서버 객체가 종료됩니다.");
});
// Express 서버 시작
http.createServer(app).listen(app.get('port'), function(){
console.log('서버가 시작되었습니다. 포트 : ' + app.get('port'));
});
5교시 (14:30-15:20)
- mysql_02.js
/**
* MySQL 데이터베이스 사용하기
*
* 웹브라우저에서 아래 주소의 페이지를 열고 웹페이지에서 요청
* (먼저 사용자 추가 후 로그인해야 함)
* http://localhost:3000/public/login2.html
* http://localhost:3000/public/adduser2.html
*
*
* npm i mysql --save
*
*/
/*
root/1234
C:\Program Files\MySQL\MySQL Server 8.0\bin>mysql -u root -p
Enter password: ****
mysql> ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '1234';
Query OK, 0 rows affected, 1 warning (0.00 sec)
*/
// Express 기본 모듈 불러오기
var express = require('express')
, http = require('http')
, path = require('path');
// Express의 미들웨어 불러오기
var bodyParser = require('body-parser')
, static = require('serve-static');
//===== MySQL 데이터베이스를 사용할 수 있도록 하는 mysql 모듈 불러오기 =====//
var mysql = require('mysql');
//===== MySQL 데이터베이스 연결 설정 =====//
var pool = mysql.createPool({
connectionLimit : 10,
host : 'localhost',
user : 'root',
password : '1234',
database : 'test',
debug : false
});
// 익스프레스 객체 생성
var app = express();
// 설정 파일에 들어있는 port 정보 사용하여 포트 설정
app.set('port', process.env.PORT || 3000);
// body-parser를 이용해 application/x-www-form-urlencoded 파싱
app.use(bodyParser.urlencoded({ extended: false }))
// body-parser를 이용해 application/json 파싱
app.use(bodyParser.json())
// public 폴더를 static으로 오픈
app.use('/public', static(path.join(__dirname, 'public')));
//===== 라우팅 함수 등록 =====//
// 라우터 객체 참조
var router = express.Router();
// 로그인 처리 함수
router.route('/process/login').post(function(req, res) {
console.log('/process/login 호출됨.');
// 요청 파라미터 확인
var paramId = req.body.id || req.query.id;
var paramPassword = req.body.password || req.query.password;
console.log('요청 파라미터 : ' + paramId + ', ' + paramPassword);
// pool 객체가 초기화된 경우, authUser 함수 호출하여 사용자 인증
if (pool) {
authUser(paramId, paramPassword, function(err, rows) {
// 조회된 레코드가 있으면 성공 응답 전송
if (rows) {
console.dir(rows);
// 조회 결과에서 사용자 이름 확인
var username = rows[0].name;
res.writeHead('200', {'Content-Type':'text/html;charset=utf8'});
res.write('<h1>로그인 성공</h1>');
res.write('<div><p>사용자 아이디 : ' + paramId + '</p></div>');
res.write('<div><p>사용자 이름 : ' + username + '</p></div>');
res.write("<br><br><a href='/public/login2.html'>다시 로그인하기</a>");
res.end();
} else { // 조회된 레코드가 없는 경우 실패 응답 전송
res.writeHead('200', {'Content-Type':'text/html;charset=utf8'});
res.write('<h1>로그인 실패</h1>');
res.write('<div><p>아이디와 패스워드를 다시 확인하십시오.</p></div>');
res.write("<br><br><a href='/public/login2.html'>다시 로그인하기</a>");
res.end();
}
});
} else { // 데이터베이스 객체가 초기화되지 않은 경우 실패 응답 전송
res.writeHead('200', {'Content-Type':'text/html;charset=utf8'});
res.write('<h2>데이터베이스 연결 실패</h2>');
res.write('<div><p>데이터베이스에 연결하지 못했습니다.</p></div>');
res.end();
}
});
// 사용자 추가 라우팅 함수
router.route('/process/adduser').post(function(req, res) {
console.log('/process/adduser 호출됨.');
var paramId = req.body.id || req.query.id;
var paramPassword = req.body.password || req.query.password;
var paramName = req.body.name || req.query.name;
var paramAge = req.body.age || req.query.age;
console.log('요청 파라미터 : ' + paramId + ', ' + paramPassword + ', ' + paramName + ', ' + paramAge);
// pool 객체가 초기화된 경우, addUser 함수 호출하여 사용자 추가
if (pool) {
addUser(paramId, paramName, paramAge, paramPassword, function(err, addedUser) {
// 결과 객체 있으면 성공 응답 전송
if (addedUser) {
console.dir(addedUser);
res.writeHead('200', {'Content-Type':'text/html;charset=utf8'});
res.write('<h2>사용자 추가 성공</h2>');
res.end();
} else {
res.writeHead('200', {'Content-Type':'text/html;charset=utf8'});
res.write('<h2>사용자 추가 실패</h2>');
res.end();
}
});
} else { // 데이터베이스 객체가 초기화되지 않은 경우 실패 응답 전송
res.writeHead('200', {'Content-Type':'text/html;charset=utf8'});
res.write('<h2>데이터베이스 연결 실패</h2>');
res.end();
}
});
// 라우터 객체 등록
app.use('/', router);
// 사용자를 인증하는 함수
var authUser = function(id, password, callback) {
console.log('authUser 호출됨 : ' + id + ', ' + password);
// 커넥션 풀에서 연결 객체를 가져옴
pool.getConnection(function(err, conn) {
var columns = ['id', 'name', 'age'];
var tablename = 'users';
// SQL 문을 실행합니다.
var exec = conn.query("select ?? from ?? where id = ? and password = ?", [columns, tablename, id, password], function(err, rows) {
conn.release(); // 반드시 해제해야 함
console.log('실행 대상 SQL : ' + exec.sql);
if (rows.length > 0) {
console.log('아이디 [%s], 패스워드 [%s] 가 일치하는 사용자 찾음.', id, password);
callback(null, rows);
} else {
console.log("일치하는 사용자를 찾지 못함.");
callback(null, null);
}
});
conn.on('error', function(err) {
console.log('데이터베이스 연결 시 에러 발생함.');
console.dir(err);
callback(err, null);
});
});
}
//사용자를 등록하는 함수
var addUser = function(id, name, age, password, callback) {
console.log('addUser 호출됨 : ' + id + ', ' + password + ', ' + name + ', ' + age);
// 커넥션 풀에서 연결 객체를 가져옴
pool.getConnection(function(err, conn) {
console.log('데이터베이스 연결 스레드 아이디 : ' + conn.threadId);
// 데이터를 객체로 만듦
var data = {id:id, name:name, age:age, password:password};
// SQL 문을 실행함
var exec = conn.query('insert into users set ?', data, function(err, result) {
conn.release(); // 반드시 해제해야 함
console.log('실행 대상 SQL : ' + exec.sql);
callback(null, result);
});
conn.on('error', function(err) {
console.log('데이터베이스 연결 시 에러 발생함.');
console.dir(err);
callback(err, null);
});
});
}
//===== 서버 시작 =====//
// 프로세스 종료 시에 데이터베이스 연결 해제
process.on('SIGTERM', function () {
console.log("프로세스가 종료됩니다.");
});
app.on('close', function () {
console.log("Express 서버 객체가 종료됩니다.");
});
// Express 서버 시작
http.createServer(app).listen(app.get('port'), function(){
console.log('서버가 시작되었습니다. 포트 : ' + app.get('port'));
});
6교시 (15:30-16:20)
- 이전에 했던 5_5_openapi_weather.py 잘 안되는 사람이 많아 다시 했음
7교시 (16:30-17:20)
- 프로젝트 수행
8교시 (17:30-18:30)
- 프로젝트 수행
Notes
- 오늘부터 오전만 수업
- 중요한 것
- ISUD (CRUD)
- Page 이동
- forward
- include
- form
- location
- redirect
- 데이터 이동
- vo
- querystring
- Ajax (req ←→res)
- jQuery, JavaScript ECMAScript 3과 5, 6 구분
- SPA, React(view)
728x90
'국비지원교육 (22.01-22.07) > 강의노트' 카테고리의 다른 글
22-06-22(수) 100일차 [Career] 이력서 클리닉 (0) | 2022.06.24 |
---|---|
22-06-21(화) 099일차 [JavaScript, React, Python, Android] React.createElement, Python XML parsing (0) | 2022.06.24 |
22-06-17(금) 097일차 [React, Node.js, Python] 리액트 기초, 파일 업로드, OpenAPI 활용 (0) | 2022.06.24 |
22-06-16(목) 096일차 [JavaScript] 자바스크립트 상속 (0) | 2022.06.24 |
22-06-15(수) 095일차 [JavaScript, Node.js, Python] 동기방식, 프로미스, 세션설정, API로 가져오기 (0) | 2022.06.24 |