오늘은 우리 비트바이트 팀의 뉴스피드 프로젝트인 OEM (오늘에러무엇)의 첫 날이다.
아이디어는 만장일치로 내가 전부터 만들고 싶었던 OEM으로 선정되었다.
발제가 끝나고 아이디어 회의가 끝난 10시부터 모두 함께 S.A.를 작성하고 prettierrc와 reset.css를 설정하였다.
뼈대와 데이터베이스 생성을 서원 님께서 맡으셨고 다같이 liveshare를 이용하여 도왔다.
팀원들의 이전 과제에서의 배움들을 공유 받으며 새로운 지식들을 많이 배울 수 있어서 좋았다.
S.A. 내용
1. 프로젝트 개요
1-1. 팀명 : 비트바이트
팀장 : 김지혜
팀원 : 김주희, 이다영, 이서원, 김태진
1-2. 프로젝트명 : OEM(오늘 에러 무엇?)
1-3. 프로젝트의 목적 또는 기능
개발자들이 각자 발생한 오늘의 에러를 공유하여
나중에 같은 상황이 발생하였을 때 참고할 수 있는 레퍼런스들을 만드는 것이 목적 (에러 백과 사전)
1-4. 프로젝트 일정
2023.06.26~2023.07.03
2023년 6월 26일 월요일 : S.A. 작성 / 작업 나누기 / 프로젝트용 깃 레포지토리 생성 / 배포 도메인 정하기 /프리티어rc, 리셋css파일 미리 생성
2023년 6월 27일 화요일 : 9:00 ~ 21:00 백엔드 기능 구현
2023년 6월 28일 수요일 : 9:00 ~ 21:00 백엔드 기능 구현
2023년 6월 29일 목요일 : 9:00 ~ 21:00 백엔드 기능 구현
2023년 6월 30일 금요일 : 9:00 ~ 21:00 백엔드 기능 구현
2023년 7월 1일 토요일 : 프론트 엔드
2023년 7월 2일 일요일 : 발표 준비
2023년 7월 3일 월요일 : 과제 제출(10시까지)
1-5. 프로젝트 운영 방식
1. 9:00 ~ 21:00 백엔드 구현에 집중한다.
2. 이 외에 시간이 되는 분은 프론트엔드 구현에 힘쓴다.
3. 10:00 ~ 11:00 회의 및 진행상황 점검 시간
4. api 호출은 thunderclient로 실행
5. 브랜치는 기능별로 나누기
1-6. 브랜치 나누기
- 백엔드 브랜치
main : 배포용
signup : 회원 가입
userme : 로그인
posts : 게시글
comments : 댓글
profile : 마이페이지
newsfeed : 뉴스피드
- 프론트엔드 브랜치
css
html
1-7. 역할 분담
김혜 : 뉴스피드
이다영 : 댓글 CRUD
김태진 : 프로필
이서원 : 게시글 CRUD
김주희 : 회원가입/로그인
1-8. 팀 규칙
1. 커밋 메시지 한글로 작성하기 ex. git commit -m "내용 한글로 작성"
2. 상황/문제/오류 보고를 수시로 하기
3. 자바스크립트 함수, 변수명은 카멜케이스 ex. let camelCase, const newsFeed
4. 상수(변하지 않는 변수)는 const로 선언하기
5. var 사용금지
6. 의견이 달라도 상대방을 먼저 이해하기 (원활한 의사소통)
7. 각자 역할에 최선을 다하기 (어려우면 빠르게 물어보기)
2. 프로젝트 기능 구현
2-1. 필수 기능
- 사용자 인증 기능 : 회원 가입, 로그인 (비밀번호 암호화)
- 프로필 : 이름, 한 줄 소개 (수정 가능)
- 게시글 CRUD 기능 : CRUD시 새로고침
- 뉴스피드페이지 : 전체 뉴스피드, 사용자별 뉴스피드, 언어별 뉴스피드
2-2. 추가 기능
- 댓글 CRUD : 새로고침
3. 와이어 프레임
3. API 명세서
4. ERD
5. 와이어 프레임
.prettierrc.js
module.exports = {
arrowParens: 'avoid',
bracketSpacing: false,
endOfLine: 'auto',
htmlWhitespaceSensitivity: 'css',
jsxBracketSameLine: false,
jsxSingleQuote: false,
printWidth: 80,
proseWrap: 'preserve',
quoteProps: 'as-needed',
semi: true,
singleQuote: true,
tabWidth: 2,
trailingComma: 'all',
useTabs: false,
vueIndentScriptAndStyle: true,
parser: '',
filepath: '',
rangeStart: 0,
rangeEnd: Infinity,
requirePragma: false,
insertPragma: false,
overrides: [
{
files: '*.json',
options: {
printWidth: 200,
},
},
],
};
reset.css
/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
models/comments.js
'use strict';
const {Model} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class Comments extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
this.belongsTo(models.Posts, {
targetKey: 'postId',
foreignKey: 'postId',
});
this.belongsTo(models.Users, {
targetKey: 'userId',
foreignKey: 'userId',
});
}
}
Comments.init(
{
commentId: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: DataTypes.INTEGER,
},
postId: {
allowNull: false,
type: DataTypes.INTEGER,
},
userId: {
allowNull: false,
type: DataTypes.INTEGER,
},
content: {
allowNull: false,
type: DataTypes.STRING,
},
createdAt: {
allowNull: false,
type: DataTypes.DATE,
defaultValue: DataTypes.NOW,
},
updatedAt: {
allowNull: false,
type: DataTypes.DATE,
defaultValue: DataTypes.NOW,
},
},
{
sequelize,
modelName: 'Comments',
},
);
return Comments;
};
models/posts.js
'use strict';
const {Model} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class Posts extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
this.belongsTo(models.Users, {
targetKey: 'userId',
foreignKey: 'userId',
});
this.hasMany(models.Comments, {
sourceKey: 'postId',
foreignKey: 'postId',
});
// define association here
}
}
Posts.init(
{
postId: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: DataTypes.INTEGER,
},
userId: {
allowNull: false,
type: DataTypes.INTEGER,
},
title: {
allowNull: false,
type: DataTypes.STRING,
},
content: {
allowNull: false,
type: DataTypes.STRING,
},
language: {
allowNull: false,
type: DataTypes.STRING,
},
createdAt: {
allowNull: false,
type: DataTypes.DATE,
defaultValue: DataTypes.NOW,
},
updatedAt: {
allowNull: false,
type: DataTypes.DATE,
defaultValue: DataTypes.NOW,
},
},
{
sequelize,
modelName: 'Posts',
},
);
return Posts;
};
models/users.js
'use strict';
const {Model} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class Users extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
this.hasMany(models.Posts, {
sourceKey: 'userId',
foreignKey: 'userId',
});
this.hasMany(models.Comments, {
sourceKey: 'userId',
foreignKey: 'userId',
});
// define association here
}
}
Users.init(
{
userId: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: DataTypes.INTEGER,
},
email: {
allowNull: false,
type: DataTypes.STRING,
unique: true,
},
nickname: {
allowNull: false,
type: DataTypes.STRING,
unique: true,
},
pw: {
allowNull: false,
type: DataTypes.STRING,
},
intro: {
allowNull: false,
type: DataTypes.STRING,
},
createdAt: {
allowNull: false,
type: DataTypes.DATE,
defaultValue: DataTypes.NOW,
},
updatedAt: {
allowNull: false,
type: DataTypes.DATE,
defaultValue: DataTypes.NOW,
},
},
{
sequelize,
modelName: 'Users',
},
);
return Users;
};
migrations/20230626082155-create-posts.js
'use strict';
const {FOREIGNKEYS} = require('sequelize/types/query-types');
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable('Posts', {
postId: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER,
},
userId: {
allowNull: false,
autoIncrement: true,
type: Sequelize.INTEGER,
references: {
model: 'Users',
key: 'userId',
},
onDelete: 'CASCADE',
},
title: {
allowNull: false,
type: Sequelize.STRING,
},
content: {
allowNull: false,
type: Sequelize.STRING,
},
language: {
allowNull: false,
type: Sequelize.STRING,
},
createdAt: {
allowNull: false,
type: Sequelize.DATE,
defaultValue: Sequelize.fn('now'),
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE,
defaultValue: Sequelize.fn('now'),
},
});
},
async down(queryInterface, Sequelize) {
await queryInterface.dropTable('Posts');
},
};
migrations/20230626082139-create-users.js
'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable('Users', {
userId: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER,
},
email: {
allowNull: false,
type: Sequelize.STRING,
unique: true,
},
nickname: {
allowNull: false,
type: Sequelize.STRING,
unique: true,
},
pw: {
allowNull: false,
type: Sequelize.STRING,
},
intro: {
allowNull: false,
type: Sequelize.STRING,
},
createdAt: {
allowNull: false,
type: Sequelize.DATE,
defaultValue: Sequelize.fn('now'),
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE,
defaultValue: Sequelize.fn('now'),
},
});
},
async down(queryInterface, Sequelize) {
await queryInterface.dropTable('Users');
},
};
app.js
const express = require('express');
const CookieParser = require('cookie-parser');
const app = express();
const port = 3000;
const postsRoutr = require('./routes/posts.route');
app.use(express.json());
app.use(CookieParser.json());
app.use('/api', [postsRoutr]);
app.listen(port, () => {
console.log(port, '로 서버가 열렸습니다!');
});
app.get('/', (req, res) => {
res.send('메인페이지');
});
'내일 배움 캠프 > OEM' 카테고리의 다른 글
프로젝트 OEM 완성 (0) | 2023.07.01 |
---|---|
OEM 프로젝트 Day5 (0) | 2023.06.30 |
OEM 프로젝트 Day4 (0) | 2023.06.29 |
OEM 프로젝트 Day3 (0) | 2023.06.28 |