저번에 서버를 닫으면 추가했던 아이디 비밀번호 이름등이 초기화되었기 때문에, 이제는 json 파일을 만들어서 서버를 닫아도 파일에서 관리를 해주고, 회원가입시 유지되도록 해봤다.
우선 UserStorage.js에서 가지고있는 id,password,name을 그대로 가져와서 databases라는 폴더를 만들고 users.json으로 파일을 만들어준 다음에, 복붙 했다.

그럼 이제 다시 라우팅부터 하나씩 따라 가겠다. 우선 routes/home/index.js 를 보면 SIGN UP 을 누를경우(POST요청)의 코드를보면,
router.post("/register", ctrl.process.register);
와 같이 되어있는데, 다시 routes/home/home.control.js로 따라간다.
process.register 이므로,
const process = {
login : (req, res) => {
const user = new User(req.body); //클라이언트 유저의 특성을 가지기 때문
const response = user.login();
return res.json(response);
},
register : (req, res) => {
const user = new User(req.body); //클라이언트 유저의 특성을 가지기 때문
const response = user.register();
return res.json(response);
},
}
register를 보면 다시 user.register에서 값을 받아오기 때문에 다시 user.js로 따라간다.
async register(){
const client = this.body;
const response = await UserStorage.save(client);
return response;
}
다시한번더 UserStorage.save로 따라가야한다. UserStorage.js로 가야한다.
우리는 파일을 읽어와야하기때문에 Save를 완전히 엎어야 된다. 우선 회원가입은 파일에 데이터를 추가하는 방식이므로, 파일 읽기,쓰기의 기능중 writeFile을 사용해야한다. 맨위에다가
const fs = require("fs").promise;
를 추가해준다. promise 를 쓴이유는 처음에 callback으로 했지만, 비동기 처리를 위해서 promise로 했다.
https://jesstory-codinglish.tistory.com/41
node.js가 어떻게 파일을 읽나 ( fs.readFile / callback, promise, async&await 구현)
과제 내용 브라우저 환경과는 다르게 node.js 앱은 내 컴퓨터에서 직접적으로 실행되기 때문에 파일을 불러오거나 저장하는 등의 일이 가능하다. node.js에서는 파일을 읽는 비동기 함수를 제공한
jesstory-codinglish.tistory.com
/파일 입력 참고/
static save(userInfo){
fs.writeFile("./src/databases/users.json", JSON.stringify(users));
return { success : true };
}
이런식으로 만들 예정이다. JSON형식으로 받아올 예정이므로, JSON.stringify를 사용해야한다. users를 JSON 형식으로 만들것이기 때문에, users를 만들어야한다. 여기서부터는 한번에 코드를 올려놓고 설명하겠다.
static #getUsers(data,isAll,fields){
const users = JSON.parse(data);
if(isAll) return users;
const newUsers = fields.reduce((newUsers, field)=>{
if (users.hasOwnProperty(field)){
newUsers[field] = users[field];
}
return newUsers;
},{});
return newUsers;
}
static getUsers(isAll, ...fields){
return fs.readFile("./src/databases/users.json")
.then((data)=>{
return this.#getUsers(data,isAll,fields)
})
.catch(console.error);
}
static async save(userInfo){
const users = await this.getUsers(true);
if(users.id.includes(userInfo.id)){
throw "이미 존재하는 아이디입니다. ";
}
users.id.push(userInfo.id);
users.name.push(userInfo.name);
users.password.push(userInfo.password);
//데이터추가
fs.writeFile("./src/databases/users.json", JSON.stringify(users));
return { success : true };
}
우선 save는 비동기 처리를 위해서 async await으로 만들어주었고, await을 하지않으면 getUsers를 받아오기전에 save를 실행하는 login.js에서 먼저 실행이 되버려서 promise가 <pending>(promise를 다읽어오지 못한상태)상태가 되기 때문이다. getUsers로 넘어가보면, 이번에는 먼저 만들어져있는 users.json을 읽어왔고, 아까 위에서
const fs = require("fs").promises;
이렇게 promise로 만들었기 때문에, 파일을 읽어오고, .then(promise 성공시 진행하는 것)을 통해 다시 #getUsers로 넘어간다.
#getUsers의 역할은 저저번시간인가에 포스팅 했었던 getUsers의 역할과 같지만 , JSON 파일을 읽어오기 때문에 우선 파싱을 해주고 추가된 점으로는, 매개변수에 프로미스된(users.json에서 읽어온) 데이터를 처리할 data와 모든값을 가져온 IsAll을 추가했다.
다시 Save로 돌아오면,
const users = await this.getUsers(true);
if(users.id.includes(userInfo.id)){
throw "이미 존재하는 아이디입니다. ";
}
이부분, 즉 JSON파일을 읽어온 users의 아이디가 userInfo(users.js에서 client>> 프론트엔드의 회원가입에 입력한 아이디)와 같다면, 문자열 자체를 throw해주었고, 아닐경우에는
users.id.push(userInfo.id);
users.name.push(userInfo.name);
users.password.push(userInfo.password);
//데이터추가
fs.writeFile("./src/databases/users.json", JSON.stringify(users));
return { success : true };
읽어온 users의 아이디 이름 비밀번호에 프론트엔드에 입력한 값을 추가해주었다. 그후에 다시 파일쓰기를 통해서 JSON형식으로 저장해주었고, success:true를 반환했다. 이제 다시 Users.js로 돌아가보면
async register(){
const client = this.body;
try{
const response = await UserStorage.save(client);
return response;
} catch(err){
return { success : false, msg: err};
}
}
save를 async await으로 만들었기 때문에 여기도 똑같이 async await으로 받아주어야하고, async await을 썻기 때문에 try catch를 사용할 수있으므로, try(성공했을경우 success:true를 그대로 반환 >>> success:true의 역할은 register.js의 post요청시 success가 true일경우 로그인으로 넘어가도록 저번포스팅에서 만들었음.) catch 일경우는 아까 문자열자체로 throw받은 msg를 출력해주고, success: false를 반환하도록 했다. 다시 컨트롤러로 돌아가보면
register : async (req, res) => {
const user = new User(req.body); //클라이언트 유저의 특성을 가지기 때문
const response = await user.register();
return res.json(response);
},
이부분 역시 async await을 써야 한다. 테스트를 해보면



이제 겨우 회원가입이 끝났다. 이제 로그인도 똑같이 파일을 읽어와서 JSON 파일과 비교해서 로그인이 가능하게 만들어야한다.
우선 다시 index.js로 가서
router.post("/login", ctrl.process.login);
이부분을 똑같이 따라가면 다시 index.js>home.control.js>Users.js>UserStorage.js로 가야한다. 과정은 생략하겠지만, Users.js를 보면
login(){
const client = this.body;
const {id, password} = UserStorage.getUserInfo(client.id);
if(id){
if(id === client.id && password === client.password){
return{ success: true};
}
return { success: false, msg : "비밀번호가 틀렸습니다."};
}
return { success: false, msg: "존재하지 않는 아이디입니다."};
}
이렇게 만들어져 있으므로, getUserInfo를 다시 싹다 갈아엎어야한다.
이번에도 코드부터 보여주고 설명하겠다.
static #getUserInfo(data,id){
const users = (JSON.parse(data));
const idx = users.id.indexOf(id);
const userInfo = Object.keys(users).reduce((newUser, info)=> {
//Object.keys == usersKeys == Object.keys(users) ==> [id,password,name]
newUser[info] = users[info][idx]; //newUser라는 오브젝트의 info==키값
return newUser;
}, {});
return userInfo;
}
static getUserInfo(id){
return fs.readFile("./src/databases/users.json")
.then((data)=>{
return this.#getUserInfo(data,id)
})
.catch(console.error);
}
getUserInfo부터 보면, return이 두개 있는 이유는, return fs.readFile는 promise객체를 반환해주는 역할이고, 밑에 .then의 반환은 우리가 진짜 필요한 유저의 정보를 반환해주는 역할이다. 이번에도 fs가 promise로 만들었기때문에 .then이 사용가능하고, 똑같이 파일을 읽어온다음에 #getUserInfo로 가보면, 이번에도 users에 읽어온 JSON 데이터를 파싱해주고 나머지는 지지난번에 포스팅한 기존의 getUserInfo와 같다. 마찬가지로 Users.js에도 login에 async await 처리를 하고, 다시 컨트롤러에도 async await 처리를 하면 끝이다.
최종 결과

다음에는 진짜 데이터베이스랑 로컬서버를 연동해서 구현해보겠다. 아마 조금? 시간이 걸릴듯 하다.
글을 나름 처음보는사람이 이해하도록 쓰고있는데 두서없이 쓰는거 같아서 전체코드도 올려두겠다.
https://github.com/netco97/Node.js/tree/main/login
GitHub - netco97/Node.js
Contribute to netco97/Node.js development by creating an account on GitHub.
github.com
'BE > Node.js' 카테고리의 다른 글
| [Node.js] React & Node.js 연결 고객 관리 시스템 (1) (2) | 2022.08.15 |
|---|---|
| [Node.js] Node.js & Mysql 연동시키기 (完) (3) | 2022.08.12 |
| [Node.js] 회원가입 기능 구현 (5) (0) | 2022.08.10 |
| [Node.js] MVC 패턴 model 분리 / 로그인 기능 구현(4) (2) | 2022.08.09 |
| [Node.js] nodemon / fetch (3) (1) | 2022.08.09 |
댓글