점호 프로그램 만들기- 과정
728x90
SMALL
 

점호 시스템

홍익대학교 기숙사 층장의 업무 자동화를 위해 점호 시스템을 만드는 과정

available-carol-098.notion.site

위의 노션의 메모를 복사한 미완성 포스트입니다.

에러

User.findOne is not a function

https://stackoverflow.com/questions/44248753/ssequelizejs-mysql-and-passportjs-user-findone-not-a-function

https://stackoverflow.com/questions/41924961/user-findone-is-not-a-function

첫번째 링크로 해결하였음.

const User = require('../models/user'); //에서
const User = require('../models').User; //로 수정

This is like this because of the way the models are dynamically exported. You can have a look at your files, models/index.js. There you will find how each model is exported in a single object. So you basically always require the models/index.js and there specify which key you want to access, in this case "User”

EJS don't escape html

Print raw html strings on EJS

https://www.digitalocean.com/community/tutorials/how-to-use-ejs-to-template-your-node-application

https://burning-camp.tistory.com/4

https://suyeoniii.tistory.com/71

이거 하면 한글은 깨져요...

시작

NoSQL

mongoDB 세팅하고 mongoose 연결한 후 postman으로 JSON형식의 data를 보내 register(회원가입)이 잘 동작하는지 mongoDB compass로 확인하였음.

user, resident schema 만들고 ObjectId 추가하여 JOIN 비슷하게 했음.

  • [ ] (NoSQL을 MySQL에 비해 제대로 공부를 하지 않아서 그게 조인이랑 어떤 차이가 있는지 알고 넘어갔어야 함)

그런데 https://rollbar.com/blog/javascript-typeerror-cannot-read-property-of-undefined/

에러로 더 이상 진행하지 못함. 이미 스키마 만들고 passport 설정해서 authentication까지 구현한 상태인데도..왜냐?

  1. 돌아가는지 중간중간 test를 건너뜀... 책을 보고하는 거니까 괜찮을 줄 알았음(너무 부끄럽다 이런 거 블로그에 적어도 되는지...............)
  2. 내가 공부한 책이 조금 구판이라서 현재의 버전에서 돌아가지 않았음

교훈

💡 1. test를 성실히 하자. 나의 코드를 너무 믿지 말자. Test 코드 작성법을 알고 적용해보자
2. 개발 공부용 책은 되도록 전자책으로, 신판으로 사자
3. 버전 관리를 섬세히 하자.

 

그래서 해당 디렉토리에 sequelize 설치해서 MySQL쓰려고 했더니, 이젠 Cannot find module err로 꼬여버림.

package.json이랑 기타 코드들 빼고 node_modules랑 package-lock.json 삭제하고 npm install하여 재설치 진행했는데도 Cannot find module 에러에서 벗어날 수 없었음... 경로가 단단히 꼬인 듯함 (단단씌!! 박선생!)

그냥 RDB를 사용하기로 결정함.

  1. 층장-사생 간의 관계를 나타내기에 RDB가 적합할 것 같기도 했고,
  2. Sequelize를 사용해보고 싶기도 했음

sequelize 장점 링크해야지... 쿼리성능이 raw보단 떨어지지만 쿼리를 튜닝하는 수준에 못 미치는 초보자로써는 일정 성능을 낼 수 있는 sequelize가 더 낫다고 블로그에서 읽었음.

MySQL

위의 결심을 단단히 먹고 새출발하는 마음으로 새출발을 함. 새 디렉토리에 전부 다시 설치하고 app.js랑 기본적인 코드만 작성하였음.

두번째 first commit... 마치 마지막 첫사랑

그런데도 cannot read property와 함께 안됨. 막막한 심정으로 강사님의 깃헙에 들어가서 app.js만 복사해서 옴. 책 신판엔 nunjucks도 포함하신듯 하여 그것도 설치함.

띠요옹~? 잘 돌아감. 역시 버전 체크는 매우매우 중요하다.

과정

Semantic URL

의미론적 URL이라는 뜻

쿼리스트링과 semantic URL을 비교한 글: https://sourceflower.tistory.com/17

semantic url을 라우팅하는 방법 : /: 기호 + key semantic url의 값에 접근하는 방법 : req객체의 query객체 대신 params객체 사용

  • [ ] 나도 URL이 의미하는 바가 잘 드러나도록 설계하기 위해 URL의 동작을 정리한 표를 짜야겠다!
  • /user/:id 가 가능하게끔 함
  • routes/page.js

`<a id="my-profile" href="/user/{{user.id}}" class="btn">내 프로필</a>`

{{}} 를 사용해서 layout.html를 바꿈

  • [ ] 그런데 왜 href="/user/<%= user.id %>" 는 안됐을까??
    • EJS cannot escape ... 로 찾아보자
    https://www.npmjs.com/package/ejs/v/3.1.5 일단 얘를 읽어보자...
  • -> 너는 넌적스를 썼으니깐...

nunjucks에 관해 정리

{%%} 사용.

https://mozilla.github.io/nunjucks/templating.html

https://stackoverflow.com/questions/33439812/what-is-the-meaning-of-tags-in-html

https://stackoverflow.com/questions/16867898/what-does-mean-in-html

보안

회원 탈퇴 시 db에서 삭제하자. 어차피 중요한 정보는 없으니까... 마찬가지로 정보 삭제 시에도 디비에서 지우자.

회원가입 페이지에 자주 사용하지 않는 비밀번호를 권장하자!!

 

use strict란

일대다? 다대다?

층장-사생 일대다? 다대다?

‘사생’이 약한 개체가 아닌 이유?

격리층에 가게되면 담당층과 격리층 층장 총 두 명의 층장이 한 사생을 중복 점호하게 되는데 다대다 관계로 연결해야할지? 아니면 격리층 층장님이 격리층이 아닌 층만 점호 목록에 포함하도록 유도해야하는지?

총원 체크만 하신다고 해서 웬만하면 일대다로도 충분할 듯.......

일대다 관계 연결하는 법

db.User.hasMany(db.Resident, { foreignKey: "user_id", sourceKey: "uid" });
db.Resident.belongsTo(db.User, { foreignKey: "user_id", sourceKey: "uid" });

Resident table에 user_id 라는 이름으로 “User.uid” 속성이 외래키로 추가된다. 아래와 같이 확인할 수 있다.(환경: mysql workbench)

db 수정은 drop database dormitory (혹은 워크벤치로 가능) 스키마 삭제 이후 create database dormitory 로 스키마를 재생성한 뒤 가능하다.

Sequelize 공식 Document - (1) Model Usage

https://velog.io/@cadenzah/sequelize-document-1

프로필이 포함된 사용자 모두를 찾고 싶다고 가정해봅시다:

User.findAndCountAll({
  include: [
     { model: Profile, required: true}
  ],
  limit: 3
});

Profile 모델에 대한 include절의 옵션이 required: true를 가지므로, 위 코드는 INNER JOIN의 결과를 반환할 것이고, 오직 프로필을 가지고 있는 사용자만이 카운트될 것입니다. 여기서 required를 제거하면, 프로필 유무와 상관 없이 모든 사용자가 카운트됩니다. where문을 include절에 추가하면 자동으로 해당 모델에 대하여 required 옵션을 추가합니다.

User.findAndCountAll({
  include: [
     { model: Profile, where: { active: true }}
  ],
  limit: 3
});

required 옵션이 없는 include 절의 추가는 결국 User 기준으로 LEFT JOIN과 같은 결과를 만들어냅니다.

위에 작성된 쿼리문은 active 상태의 프로필을 가진 사용자만을 카운트합니다. include절이 where 문을 포함할 경우 required는 암묵적으로 true 값이 주어지기 때문이죠.

findAndCountAll에 인자로 전달되는 옵션 객체는 findAll과 동일합니다.

Mysql errno:1452 참조 무결성 제약조건 위반 해결

code: 'ER_NO_REFERENCED_ROW_2', errno: 1452, sqlState: '23000', sqlMessage: 'Cannot add or update a child row: a foreign key constraint fails (dormitory.residents, CONSTRAINT residents_ibfk_1 FOREIGN KEY (user_id) REFERENCES users (uid) ON DELETE SET NULL ON UPDATE CASCADE)', sql: 'INSERT INTO residents (id,room,name,user_id) VALUES (DEFAULT,?,?,?);', parameters: [ '1901', '이예은', 1 ] }, sql: 'INSERT INTO residents (id,room,name,user_id) VALUES (DEFAULT,?,?,?);', parameters: [ '1901', '이예은', 1 ], table: 'users', fields: [ 'user_id' ], value: 1, index: 'residents_ibfk_1', reltype: 'child' }

model을 보면, uid 라는 속성을 user의 기본키이자 resident의 외래키로 설정하였다.

그렇다면 당연히 새로운 resident객체를 생성할때는 user.uid를 속성에 추가해야 한다.

  1. 내가 설계한 DB를 완전히 이해하고 있지 않아 실수했다.
  2. 변수명(column 이름)을 제대로 짓지 않아 실수했다.

→ 변수명 제대로 짓자.

try {
    await Resident.create({
      room: room,
      name: name,
      user_id: req.params.id,
    });
    return res.redirect(`/resident/${id}/list`);
  } catch (err) {
    console.error(err);
    return next(err);
  }
try {
    await Resident.create({
      room: room,
      name: name,
      user_id: req.user.uid,
    });
    return res.redirect(`/resident/${id}/list`);
  } catch (err) {
    console.error(err);
    return next(err);
  }

sh:1 nodemon not found

"start": "node app.js",
"start:dev": "nodemon app.js"

config.json → config.js

🔔주의사항🔔

.env 파일에서 관리하고 있는 변수들은 github에 올라가면 안되는 정보들이 있기 때문에 보통 .gitignore에서 관리되고 있다. 하지만 heroku는 .env 파일의 정보가 있어야 한다. 그럴 경우엔 해당 App의 settings에서 Config Vars에 추가해 주면 된다.

출처:

https://rkdvnfma90.tistory.com/224

config/config.json

require("dotenv").config();
const env = process.env;

const development = {
  username: env.MYSQL_USERNAME,
  //env.MYSQL_USERNAME은 불러오고자 하는 데이터의 키값이므로 자유롭게 이름설정이 가능하다.
  password: env.MYSQL_PASSWORD,
  database: env.MYSQL_DATABASE,
  host: env.MYSQL_HOST,
  dialect: "mysql",
  //port: env.MYSQL_PORT
};

const production = {
  username: env.MYSQL_USERNAME,
  password: env.MYSQL_PASSWORD,
  database: env.MYSQL_DATABASE,
  host: env.MYSQL_HOST,
  dialect: "mysql",
  //port: env.MYSQL_PORT
};

const test = {
  username: env.MYSQL_USERNAME,
  password: env.MYSQL_PASSWORD,
  database: env.MYSQL_DATABASE_TEST,
  host: env.MYSQL_HOST,
  dialect: "mysql",
  //port: env.MYSQL_PORT
};

module.exports = { development, production, test };

connect ECONNREFUSED 127.0.0.1:3306

https://stackoverflow.com/questions/62899030/heroku-error-connect-econnrefused-127-0-0-13306-when-trying-to-deploy-expres

localhost는 배포 시 작동하지 않습니다.

 

728x90
LIST