[node.js] View Template 적용하기(1) - Semantic UI(반응형), EJS 사용
by 무작정 개발2022.04.07(73일 차)
이번에는 View Template 중 하나인 Semantic UI에 대해 정리할 것이다.
그리고 이전 글에 정리한 모듈 분리 소스 코드를 응용할 것이고, 추가적인 모듈 분리를 더 할 것이다.
오늘의 수업 내용
Semantic UI (시멘틱 UI)
View Template란 버튼, 아이콘, 카드, 피드, 체크 박스 등 수많은 UI를 HTML로 제공해주는 프레임워크이다.
대표적으로 부트스트랩(BootStrap), Ulkit, 시멘틱 UI(Semantic UI)가 있다.
그중에 Semantic UI를 배워보겠다.
1. Semantic UI 패키지 다운
Semantic UI 사이트에 들어가서 Get Started 버튼을 클릭 후 각자 운영체제에 맞게 설치를 한다.
나는 windows 환경이라 windows 버전을 설치했다.
2. 압축 풀기
이전에 만들었던 ModuleExe 프로젝트 안에 public1 폴더를 만들고, 그 안에 date 폴더를 만들고 그 안에
min.js, min.css 파일만 압출을 풀어서 넣어주었다.
나머지 파일들은 이번에 정리할 파일들이다.
public1 > date > semantic.min.css / semantic.min.js
3. 사용 방법(Tip)
홈페이지 왼쪽에서 가져오고 싶은 테마를 선택하고 그중에 원하는 UI를 찾으면 위에 표시된 < >를 클릭하면
그 UI에 관련된 소스 코드가 나온다. 복사해서 사용하면 끝.
Semantic UI를 사용하여 웹 페이지 만들기
이전 글에서 정리한 소스 코드들을 사용해서 웹 페이지를 만들 것이다.
이전과 기능은 같다. 로그인, 회원가입, 회원 리스트 출력, 등이 있다.
app2.js를 실행시키는 되고, app2에서 모든 소스 코드를 작성하였는데 모듈을 분리해서 router폴더, database 폴더에
분리한 파일들을 작성해서 넣어주었다.
public1 안의 data 폴더에는 직접 만든 css파일과, 시멘 티 UI에서 다운로드한 파일을 넣어주었다.
image 폴더에는 사용할 이미지를 넣어주었고, views에서는 ejs 파일을 넣어주었다.
ejs는 아래서 자세히 설명할 것이다.
ejs
ejs란 라우터에서 뷰 엔진을 호출할 수 있는 모듈이다.
뷰 엔진은 node.js이 응답 페이지를 독립적으로 작성하고 활용할 수 있도록 제공되는 시스템이고,
그중 하나가 ejs이다.
뷰 엔진 또한 모듈이기에 파일 탐색기에서 ModuleExe 폴더 우클릭 CmdHire 클릭을 해서 cmd창을 연다.
$ npm install ejs
위의 명령어를 cmd 창에 작성하고 엔터(Enter)를 누르면 정상적으로 모듈이 설치된다.
위의 소스 코드만 다르고 나머지 app2.js는 이전과 거의 동일하다.
로그인을 예를 들어 설명해보겠다.
위의 사진(app2.js) 부분에서 view 엔진을 설정해줌으로 views 파일로 연결되도록 하였다.
기존에는 로그인 성공 시 나오는 페이지를 위의 소스 코드처럼 작성하였다.
이번에는 ejs를 사용해서 따로 login.ejs 파일을 생성해서 위의 부분에 저렇게
소스를 작성할 필요가 없어진다.
1. app2.js 작성
- 이전 글에서 학습한 app2.js에서 추가적으로 모듈 분리를 해서 소스 코드 수정
- ejs 뷰 엔진 사용
//암호화
//npm install crypto --save
//crypto 모듈은 pwd + salt(key)를 합쳐서 암호화 //salt(key)는 사용자정의
//Express 기본 모듈
require("dotenv").config();
var express = require("express");
var http = require("http");
var path = require("path");
//var bodyParser = require("body-parser"); //예전에는 bodyParser를 호출해야 했지만 express에 내장이 되어 안써도 괜춘
var serveStatic = require("serve-static"); //특정 폴더를 패스로 접근 가능하게 하는것.
var expressErrorHandler = require("express-error-handler");
var expressSession = require("express-session");
//var mongoose = require("mongoose"); database.js안에 만들어줬기에 필요x
var user = require("./router/user"); //router에 user.js파일
var config = require("./config");
var database = require("./database/database");
var routerLoader = require("./router/routerLoader");
//익스프레스 객체 생성
var app = express();
//뷰 엔진 설정
app.set("views",__dirname + "/views");
app.set("view engine","ejs"); //엔진을 ejs로 할꺼얌
console.log("뷰엔진이 ejs로 설정되었습니다.");
app.set("port",process.env.PORT||config.serverPort);
app.use(express.urlencoded({extended:false}));
app.use("/public1",serveStatic(path.join(__dirname,"public1"))); //public (실제)폴더의 이름을 써준것
//사용자정의
app.use(expressSession({
secret:"my key",
resave:true,
saveUninitialized:true
}));
//-----------------------------------------------------------------------------
//라우터 객체 생성
var router = express.Router();
routerLoader.init(app,router);
var errorHandler = expressErrorHandler({
static: { //미리 메모리상에 올려둔것
"404":"./public/404.html" //404에러가 뜨면 public에 404.html로 가라
}
});
app.use(expressErrorHandler.httpError(404));
app.use(errorHandler); //변수명 담아줌
//Express 서버 시작
//var server = http.createServer();
http.createServer(app).listen(app.get("port"),function(){ //위에 set으로 넣어둔 port를 get으로 가져온거
console.log("익스프레스 서버 on~~ 포트번호는:" + app.get("port"));
//DB연결 함수 호출
database.init(app,config);
});
2. database 폴더 안에 있는 database.js, userSchema.js
- app2.js에서 모듈을 분리해서 작성하였다.
database.js
var mongoose = require("mongoose");
//database 객체에 db,schema,model을 추가
var database = {};
//app : express객체
//config : config.js
database.init = function(app,config) {
connect(app,config); //connect라는 함수에 받은 app,config와 함께 넘겨준다.
}
function connect(app,config) {
console.log("connect 호출됨..");
//연결
mongoose.connect(config.dbUrl);
database = mongoose.connection;
database.on("open",function(){
console.log("데이터베이스가 연결되었습니다: " + config.dbUrl);
// config.js에 등록된 스키마 및 모델 객체 생성(읽어온다)
createSchema(app,config); // 함수로 호출할것임
});
database.on("error",console.error.bind(console,"몽구스 연결 에러..."));
database.on("disconnected",function(){
console.log("DB연결이 끊겼습니다 5초후 재연결 합니다");
setInterval(connectDB(),5000);
});
}
//config에 정의된 스키마 및 모델 객체 생성
function createSchema(app,config) {
var schemaLen = config.dbSchemas.length;
for(var i=0; i<schemaLen; i++) {
var curItem = config.dbSchemas[i];
//스키마 생성 : 함수를 아예 써버리고 읽어오면 변수가 함수가 된다.
var curSchema = require(curItem.file).createSchema(mongoose); //이렇게 하면 curSchema가 스키마가 된다.
//모델 생성 : 다 이렇게 변수처리 해버리는거에요
var curModel = mongoose.model(curItem.collection, curSchema);
//database 객체에 스키마와 모델을 추가한 것
database[curItem.schemaName] = curSchema; // 이것은 config의 UserSchema이다,
database[curItem.modelName] = curModel; // 이것은 config의 modelName
}
app.set("database",database); //따옴표 변수명, 그리고 위에서 만든 2개의 데이터베이스를 넣는다. /여기엔 db+schema+model 3개가 들어가 있는 것이다.
}
module.exports = database; //외부에서 쓸수있게 만듬
userSchema.js
var crypto = require("crypto");
//app1.js에 있는 스키를 따로 분리해서 userSchema.js 파일로 만들었다.
//- 여기엔 스키마 분리
// 자바에서 따지면 Schema 라는 객체
var Schema = {};
Schema.createSchema = function(mongoose){
UserSchema = mongoose.Schema({
id:{type:String,required:true,unique:true}, // 아이디에 들어가는 값은 문자고 required 반드시 필요하고 , 유의값 - > 프라이머리키가 됨
hashed_password:{type:String,required:true}, // 암호화된 패스워드 이다. 그러므로 hashed_pwd로 바꿈
// 암호화 + salt , salt키를 저장해야함 안하면 High문자를 저장하면 a123를 넣으면 high라는애를 가지고 와서 붙여서 암호화를 해야하는데 암호화된게 hashed_pwd로 들어가는데
salt:{type:String,required:true},// 다음에도 123를 붙이는데 앞에 salt키가 있어야되므로 항상 보관되어 있어야 한다. 암호화시킬때 임의로 붙이는 키값이다 . 반드시 필요하므로 required : true
name:{type:String},
age:{type:Number,'default':20},
created:{type:Date,index:{unique:false},'default':Date.now} // unique:false 똑같은값이 들어가도 상관없다 , 날짜를 안넣으면 오늘날짜
});
UserSchema
.virtual("pwd") // 암호는 뭘로 만들건가 password로 만듬 , password값으로 넣겠다
.set(function(pwd){
this._password = pwd; // 패스워드를 넣을것이다. _password 변수이다. 바꾸면 안됨
this.salt = this.makeSalt(); // 아래에서 만든 salt키값을 _salt에 넣을것이다.
this.hashed_password = this.encryptPassword(pwd); // 암호화를 시키는 메소드이다 밑에 만들것임
})
.get(function(){
return this._password; // 패스워드 반환값 이 값을 hashed_password:{type:String,required:true} 에 넣는다
})
// 스키마 객체에 메소드를 추가 : method() 방법
UserSchema.method("makeSalt",function(){ // 메소드를 만들면서 makeSalt를 호출함
// return "hi"; // hi붙이고싶다 hi라는 문자가 this.makeSalt()에서 호출되는데 salt에 hi가 들어감
// db에 salt:{type:String,required:true} 들어감
// hi 123 A , hi 456 B , hi 787 C
// 암호화할때 hi만 붙이게되면 패스워드를 암호화하는 방식이 너무 단순
// A라는 사람이 로그인할때 hi라고 하지않고 abc123
// B라는 사람이 접속하면 123456
// c라는 사람이 접속하면 265189
// 그 사람만의 고유 salt를 만드는 것이다.
// id pw salt
// A 123 abc = XXX 암호화된 결과물
// B 456 129
// C 789 369
// 즉 독자적인 salt키를 갖게 된다.
// pw + salt 를 암호화된 pwd와 비교하게 된다 .
// 실제패스워드를 저장하지않고 암호화된 패스워드 저장함
// 각자의 salt키도 저장해야 한다.
// 지금은 시시각각 변하는 난수값을 넣어서 알아내기 쉽지 않다
console.log("date : " + new Date().valueOf()); // 초에따라 일련숫자 12341242 나옴
console.log("math : " + Math.random()); // 0.1234214242 매번바뀜
return Math.round((new Date().valueOf() * Math.random())) + ""; // 랜덤함수를 그순간에 만들어서 뒤에 + 붙여서 문자가됨 그러면 패스워드랑 붙을수있다. String값이됨
})
// 암호화 작업을 하는 곳
UserSchema.method("encryptPassword",function(inputPwd,inSalt){ // 암호화를 시키려면 입력되는 패스워드 inputPwd 를 넣음
// 암호화 하려면 salt가 필요하므로 inSalt
// 그러면 들어가는 패스워드와 salt 가 합쳐져서 암호화가 된다.
// 이미 암호화가 되어있으면 로그인이 되어있으면 123 입력하면
// 얘를 암호화 해서 기존에 저장되어있는 암호화된 데이터를 같냐 해서 비교작업을 해줘야 하다.
if(inSalt) { // inSalt가 있으면
// 암호화 작업
return crypto.createHmac("sha1",inSalt) // sha1 암호화 용어인데 쉬바라고 읽는다. 1은 암호화하는 등급이다. 암호화하는 모듈로써 모든곳에서 다쓴다
.update(inputPwd).digest("hex"); // hex 헥사라고 읽음
} else {
// 안주게 되면 this 자체적으로 salt키를가지고 있으면 그걸써라
return crypto.createHmac("sha1",this.salt) // sha1 암호화 용어인데 쉬바라고 읽는다. 1은 암호화하는 등급이다. 암호화하는 모듈로써 모든곳에서 다쓴다
.update(inputPwd).digest("hex");
}
})
// 123를 암호화해서 암호화된 데이터를 비교해야되는 메소드를 만들어야한다.
// 로그인할때 암호화된 PWD비교
UserSchema.method("authenticate",function(inputPwd,inSalt,hashed_password){ // 사용자가 입력할 패스워드가 필요
// inputPwd + insalt 가 들어오면 123high 해서 암호화한다.
// 암호화된 데이터가 db에 저장되어있는 암호화된 데이터를 읽어내야 한다
// 두개를 비교하는데 inputPwd , insalt 와 db에서 꺼내온 암호화된 패스워드 hashed_password가 필요하다
if(inSalt) { // 설트키 가 있으면
console.log("사용자 입력 pwd: " + inputPwd); // abc
console.log("암호화된 pwd: " + this.encryptPassword(inputPwd,inSalt)); // 암호화된 패스워드를 보여줄것이다. abc가 xxxxxxx
console.log("DB에 저장되어 있는 pwd: " + hashed_password);// db에 저장되어있는 암호화된 패스워드 abc가 db에저장된 암호화된 데이터
// a 123 + salt 와 db에서 읽어온것과 일치하면 true 반환
return this.encryptPassword(inputPwd,inSalt)==hashed_password; // 암호화된 시킨것과 db에 저장된것과 일치하나? true , false
} else { // 설트키 가 없으면 패스워드만 감
// 위에서 부르면 function 에 inSalt가 없으면 this.Salt 로 감
console.log("사용자 입력 pwd: " + inputPwd); // abc
console.log("암호화된 pwd: " + this.encryptPassword(inputPwd,inSalt)); // 암호화된 패스워드를 보여줄것이다. abc가 xxxxxxx
console.log("DB에 저장되어 있는 pwd: " + hashed_password);// db에 저장되어있는 암호화된 패스워드 abc가 db에저장된 암호화된 데이터
return this.encryptPassword(inputPwd)==this.hashed_password;
}
});
//스키마 객체에 메소드를 추가(방법: static(), method() )
//로그인에서 사용
// findById는 메소드의 이름 뒤에 콜백함수
// static를 써서 메소드
UserSchema.static("findById",function(id,callback){ // 아이디를 가지고 select 했을때 들어갔을때 무조건 작업이 끝났을때 결과가 콜백으로 간다.
return this.find({id:id},callback); // id를 찾아라 찾던안찾던 callback함수 실행해라
})
// 전체 데이터 사용
UserSchema.static("findAll",function(callback){ // findAll 메소드를
return this.find({}, callback); // 전체데이터는 조건이 없어서 {} 몽땅가져옴
})
console.log("UserSchema 정의함.");
return UserSchema;
}
module.exports = Schema; // 외부에서 이 파일을 엑세스 할수있다.
3. router 폴더 안에 있는 routerLoader.js, user.js
routerLoader.js
var config = require("../config");
var routerLoader = {};
routerLoader.init = function(app,router) {
console.log("routerLoader 호출됨..");
return initRouters(app,router);
}
function initRouters(app,router) {
var infoLen = config.routeInfo.length; //3
for(var i=0; i<infoLen; i++) {
var curItem = config.routeInfo[i];
var curModule = require(curItem.file); //user.js 읽어옴
//라우팅
if(curItem.type=="get") {
//router.route("/process/login").get(user.login);
router.route(curItem.path).get(curModule[curItem.method]);
}else if(curItem.type=='post') {
router.route(curItem.path).post(curModule[curItem.method]);
}else {
router.route(curItem.path).post(curModule[curItem.method]);
}
}
console.log("라우팅 모듈 설정");
//라우터 객체 등록
app.use("/",router);
}
//외부에서 쓰기 위해 등록
module.exports = routerLoader;
user.js
//라우터 안에서 함수를 호출하기 때문에 2개는 같은 공간에 있어야 한다.
//로그인 라우터
var login = function(req,res) {
console.log("/process/login 호출..");
var id = req.body.id;
var pwd = req.body.pwd;
var database = req.app.get("database");
if(database){
authUser(database, id, pwd, function(err,result){
if(err) {throw err;}
if(result){
var userName = result[0].name;
var context = {userId:id,userName:userName};
req.app.render("login",context,function(err,html){//뷰엔진을 꺼냈다. 그런데 만약 못 가지고 왔다?
if(err){
res.writeHead("200",{"Content-type":"text/html;charset=utf-8"});
res.write("<h1>뷰 렌더링 하는데.. 에러났어.</h1>")
res.write("<br/><br/><a href='/public1/login.html'>다시 로그인</a>");
res.end();
return;
}
res.end(html);
});
}else{
res.writeHead("200",{"Content-type":"text/html;charset=utf-8"});
res.write("<meta name='viewport' content='width=device-width,height=device-height,initial-scale=1'>");
res.write("<h1>로그인 실패</h1>")
res.write("<div>아이디 또는 패스워드를 확인하세요</div>");
res.write("<br/><br/><a href='/public1/login.html'>다시 로그인</a>");
res.end();
}
});
}else{
res.writeHead("200",{"Content-type":"text/html;charset=utf-8"});
res.write("<meta name='viewport' content='width=device-width,height=device-height,initial-scale=1'>");
res.write("<h1>데이터베이스 연결 실패</h1>")
res.write("<div>데이터베이스를 연결하지 못했습니다.</div>");
res.end();
}
};
//회원가입 라우터
var addUsers = function(req,res) {
console.log("/process/addUser 호출..");
var database = req.app.get("database");
var id = req.body.id;
var pwd = req.body.pwd;
var name = req.body.name;
if(database){
addUser(database, id, pwd, name, function(err,result){
if(err) {throw err;}
if(result){
res.writeHead("200",{"Content-type":"text/html;charset=utf-8"});
res.write("<meta name='viewport' content='width=device-width,height=device-height,initial-scale=1'>");
res.write("<h1>사용자 추가 성공</h1>")
res.write("<br/><br/><a href='/public1/login.html'>다시 로그인</a>");
res.end();
}else{
res.writeHead("200",{"Content-type":"text/html;charset=utf-8"});
res.write("<meta name='viewport' content='width=device-width,height=device-height,initial-scale=1'>");
res.write("<h1>사용자 추가 실패</h1>")
res.write("<br/><br/><a href='/public1/login.html'>다시 로그인</a>");
res.end();
}
});
}else{
res.writeHead("200",{"Content-type":"text/html;charset=utf-8"});
res.write("<meta name='viewport' content='width=device-width,height=device-height,initial-scale=1'>");
res.write("<h1>데이터베이스 연결 실패</h1>")
res.write("<div>데이터베이스를 연결하지 못했습니다.</div>");
res.end();
}
}
//리스트 라우터
var listUser = function(req,res) {
console.log("/process/listUser 호출됨")
var database = req.app.get("database");
// db에서 데이터 가져옴
if(database) { // 데이터베이스 객체가 있으면 연결해서 데이터 가져옴
/*UserSchema.static("findAll",function(callback){
return this.find({}, callback);
})*/
// 함수 호출하면됨
database.UserModel.findAll(function(err,result){ // 모든데이터 가져오는 메소드 static("findAll", -> find{} 넘겨주는값이 필요없음 function(err,result) 로 처리
if(err) { // 에러가 발생하면
res.writeHead("200",{"Content-type":"text/html;charset=utf-8"});
res.write("<meta name='viewport' content='width=device-width,height=device-height,initial-scale=1'>");
res.write("<h2>사용자 리스트 조회중 에러 발생</h2>")
res.write("<br/><br/><a href='/public1/login.html'>다시 로그인</a>");
res.end();
return; // 밑에 실행하지 않게 return 꼭 써줌
}
if(result){ // 정상적으로 데이터를 가지고 왔을때
var context = {result:result};
req.app.render("listUser.ejs",context,function(err,html) {
if(err){
res.writeHead("200",{"Content-type":"text/html;charset=utf-8"});
res.write("<h1>뷰 렌더링 하는데.. 에러났어.</h1>")
res.end();
return;
}
res.end(html);
});
} else {
// 데이터가 없을 경우
res.writeHead("200",{"Content-type":"text/html;charset=utf-8"});
res.write("<meta name='viewport' content='width=device-width,height=device-height,initial-scale=1'>");
res.write("<h2>사용자 조회 실패</h2>")
res.write("<br/><br/><a href='/public1/login.html'>다시 로그인</a>");
res.end();
}
})
}
}
//사용자를 인증하는 함수
var authUser = function(database,id,pwd,callback){
console.log("authUser 함수 호출..");
//id가 function(id,callback) 에서 find({id:id}로 찾아 오게되면서 결과가 callback함수가 에러면 err 잘나오면result 로 들어감
// 즉 , 콜백함수는 id를 찾았을경우 실행된결과를 받음
//아이디와 비밀번호 검색
database.UserModel.findById(id, function(err,result){
// 에러가 있으면 이걸 실행
if(err) {
callback(err,null);
return;
}
//데이터가 있을경우
if(result.length>0){
console.log("아이디와 일치하는 사용자 찾음")
// 무조건 배열로 넘어옴
// ._doc = 도큐먼트 DB 값을 비교
// 암호화된 데이터 비교
var user = new database.UserModel({id:id});
var authenticated =
user.authenticate(pwd,result[0]._doc.salt,result[0]._doc.hashed_password); // 위에 메소드 UserSchema.method("authenticate",function(inputPwd,inSalt,hashed_password) 호출
// pwd , salt키 = db암호화된 비교작업
// authenticated 에는 true 아니면 fasle 가 들어가게됨
} if(authenticated){ // 사용자가 입력한 pwd가 일치하나 ?
console.log("비밀번호 일치함")
callback(null,result); // 에러는 없으니 null ,데이터 있으니 result 는 넣어줌
} else {
console.log("비밀번호가 일치하지 않음");
callback(null,null); // 에러없고 결과가 없으니 null , null
}
/*} else {
console.log("아이디와 일치하는 데이터가 없습니다.");
callback(null,null); // 에러없고 결과가 없으니 null , null
}*/
}); //여기까진 라우터
}
//---------------------------------
//사용자를 추가하는 함수
var addUser = function(database,id,pwd,name,callback){
console.log("addUser 함수 호출..");
var users = new database.UserModel({"id":id, "pwd":pwd, "name":name});
users.save(function(err,result){
if(err){
callback(err,null);
return;
}
if(result){
console.log("사용자 추가..");
}else{
console.log("사용자 추가 실패..");
}
callback(null,result);
});
};
//위의 함수들을 외부에서 사용하기 위해
//module.exports.init = init; //이걸 호출하는 순간 DB,스키마 등이 넘어오는 것
module.exports.login = login;
module.exports.addUser = addUsers;
module.exports.listUser = listUser;
4. views 폴더 안에 있는 listUser.ejs, login.ejs
- ejs 사용 / JSTL를 사용할 때와 비슷한 느낌이다.
listUser.ejs
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,height=device-height,initial-scale=1">
<title>사용자 리스트 페이지</title>
</head>
<body>
<h1>사용자 리스트(View - ejs)</h1>
<div>
<ul>
<%for(var i=0; i<result.length; i++) { %>
<li>
#<%=i+1 %> -
아이디: <%=result[i]._doc.id %>,
이름: <%=result[i]._doc.name %>,
나이: <%=result[i]._doc.age %>
</li>
<%} %>
</ul>
</div>
<br/><br/>
<a href="/public1/listUser.html">다시 요청</a>
</body>
</html>
login.ejs
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,height=device-height,initial-scale=1">
<title>로그인 성공 페이지</title>
</head>
<body>
<h1>로그인 성공(View - ejs)</h1>
<div>사용자 아이디: <%=userId%></div>
<div>사용자 이름: <%=userName%></div>
<br/><br/>
<a href="/public1/login.html">다시 로그인</a>
</body>
</html>
5. html 파일과 css 파일
- html 파일 소스들은 시멘틱 UI에서 가져온 소스 + 기존에 사용한 소스들을 응용한 것이라 설명은 생략
- css파일은 하단에 첨부
- CSS 파일
- HTML 파일
6. 실행 화면
위의 3개의 화면들은 시멘틱 UI를 이용하여 만든 반응형 웹 페이지이다.
페이지 창 크기에 맞춰서 사이즈가 맞춰진다. 모바일 또한 모바일 화면에 맞춰서
바뀐다.
자세한 소스 코드는 하단 깃허브 링크 참고
https://github.com/chaehyuenwoo/node.js/tree/main/ModuleExe
'Back-End > node.js' 카테고리의 다른 글
[node.js] Socket.io 모듈을 이용한 채팅 프로그램 만들기 (0) | 2022.04.11 |
---|---|
[node.js] View Template 적용하기(2) - Semantic UI(반응형), Jade 사용 (0) | 2022.04.09 |
[node.js] 암호화(crypto) 모듈, 모듈 분리 방법(1) (0) | 2022.04.06 |
[node.js] node.js + Oracle DB 연결하기 (1) | 2022.04.06 |
[node.js] mongoose 모듈을 활용한 관계형 DB (0) | 2022.04.05 |
블로그의 정보
무작정 개발
무작정 개발