BE_Westagram project_11.4

송철진·2022년 11월 3일
0

Toy Project

목록 보기
3/10

초기 환경설정이 워낙 복잡하고 할게 하도 많아서 삭제하고 다시 만들기를 몇번이나 했는지.. 또 삭제하고 다시 만들 가능성이 있기에 작업순서를 간단하게 정리해보았다.

req.params, req.body
링크

#1 초기 환경설정

  1. 깃허브 클론하기

  2. 폴더 만들기 : mkdir 내이름

  3. 폴더 들어가기 : cd 내이름

  4. package.json 만들기 : npm init -y

  5. package.json에서 내용 넣기

  6. express 설치하기

npm install express
  1. nodemon 설치하기
npm install -g nodemon
npm install --save-dev nodemon
  1. dotenv 설치하기
npm install dotenv
  1. morgan 설치하기
npm install morgan
  1. typeorm 설치하기
npm install typeorm
  1. mysql 설치하기
npm install mysql
npm install -g mysql
  1. app.js 파일 생성하고 핑퐁 코드 넣기
touch app.js 
  1. .env파일, .env.sample파일, .gitignore파일 생성하기
touch .env
touch .env.sample
touch .gitignore
  1. .env파일 값 입력하기
  • 한글 부분을 다시 입력해야함!
  • mysql 들어가서 CREATE DATABASE 데이터베이스명 으로 생성해둘 것!
DATABASE_URL = "mysql://계정명:비밀번호@127.0.0.1:3306/데이터베이스명"

PORT = 포트번호

TYPEORM_CONNECTION = mysql
TYPEORM_HOST = 127.0.0.1
TYPEORM_USERNAME = 계정명
TYPEORM_PASSWORD = 비밀번호
TYPEORM_DATABASE = 데이터베이스명
TYPEORM_PORT = 3306
TYPEORM_LOGGING = TRUE
  1. .env.sample파일 값 입력하기
  • 이건 제3자에게 알리기 위한 용도이므로 그대로 써도 됨!
DATABASE_URL = URL

PORT = YOUR PORT NUMBER

TYPEORM_CONNECTION = mysql
TYPEORM_HOST = 127.0.0.1
TYPEORM_USERNAME = username
TYPEORM_PASSWORD = myPassword
TYPEORM_DATABASE = myDatabase
TYPEORM_PORT = 3306
TYPEORM_LOGGING = TRUE
  1. .gitignore파일 값 입력하기
  • https://gitignore.io 사이트를 참조하여 개인컴퓨터 환경에 맞는 내용을 생성, 복붙한다!
# Created by https://www.toptal.com/developers/gitignore/api/node,macos,dotenv
# Edit at https://www.toptal.com/developers/gitignore?templates=node,macos,dotenv

/db/schema.sql

### dotenv ###
.env

...

npm start 로 app.js 동작 확인하기

  1. db 폴더 생성하기
mkdir db
  1. dbmate 글로벌로 설치하기
npm install -g dbmate
  1. dbmate로 각 테이블 마이그레이션 파일 생성하기
dbmate new create_users_table
dbmate new create_posts_table
dbmate new create_likes_table
dbmate new create_comments_table
  1. 각 테이블 마이그레이션 파일 내용 입력하기
  • users
-- migrate:up
CREATE TABLE users 
(
  id INT NOT NULL AUTO_INCREMENT,                        
  name VARCHAR(50) NOT NULL,                             
  email VARCHAR(200) NOT NULL,
  profile_image VARCHAR(1000) NULL,    
  password VARCHAR(200) NOT NULL,
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,           
  updated_at TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP, 
  PRIMARY KEY (id)                                       
);

-- migrate:down
DROP TABLE users;
  • posts
-- migrate:up
CREATE TABLE posts
(
  id INT NOT NULL AUTO_INCREMENT,
  title VARCHAR(100) NOT NULL,
  content VARCHAR(3000) NULL,
  image_url VARCHAR(1000) NULL,
  user_id INT NOT NULL,
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (id),
  FOREIGN KEY (user_id) REFERENCES users (id) 
);

-- migrate:down
DROP TABLE posts;
  • likes
-- migrate:up
CREATE TABLE likes
(
  id INT NOT NULL AUTO_INCREMENT,
  user_id INT NOT NULL,
  post_id INT NOT NULL,
  PRIMARY KEY (id),
  FOREIGN KEY (user_id) REFERENCES users (id),
  FOREIGN KEY (post_id) REFERENCES posts (id)
);

-- migrate:down
DROP TABLE likes;
  • comments
-- migrate:up
CREATE TABLE comments
(
  id INT NOT NULL AUTO_INCREMENT,
  user_id INT NOT NULL,
  post_id INT NOT NULL,
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (id),
  FOREIGN KEY (user_id) REFERENCES users (id),
  FOREIGN KEY (post_id) REFERENCES posts (id)
);

-- migrate:down
DROP TABLE comments;
  1. 마이그레이션 파일을 직접 테이블로 옮기는 작업 실행
dbmate up
  1. git 올리기
git branch 내이름/assignment1 
git checkout 내이름/assignment1
git add .
git commit -m "[39기 내이름] Assignment 1: Express API - 초기환경세팅"
git push origin 내이름/assignment1
  1. 깃허브 들어가서
    compare & pull request 버튼 누르고
    PR 제목 & 본문 작성하고
    create pull request 버튼 누르기

#2

데이터베이스에 미리 데이터 입력하기

  • users
INSERT INTO users (name, email, profile_image, password, created_at, updated_at) VALUES ("Rebekah", "Glover12345@email.com", "https://github.com/amacneil/dbmate#command-line-options", "password", "2022-07-16 14:15:21", NULL);
INSERT INTO users (name, email, profile_image, password, created_at, updated_at) VALUES ("Fabian", "O'Connell12345@email.com", "https://github.com/amacneil/dbmate#command-line-options", "password1", "2022-07-16 14:15:21", NULL);
INSERT INTO users (name, email, profile_image, password, created_at, updated_at) VALUES ("Elenor", "Skiles12345@email.com", "https://github.com/amacneil/dbmate#command-line-options", "password2", "2022-07-16 14:15:21", NULL);
INSERT INTO users (name, email, profile_image, password, created_at, updated_at) VALUES ("Madge", "Quitzon12345@email.com", "https://github.com/amacneil/dbmate#command-line-options", "password3", "2022-07-16 14:15:21", NULL);

  • posts
INSERT INTO posts (title, content, user_id, created_at, updated_at) VALUES ("위코드 1일차", "HTML과 CSS 익숙해지기..", 1, "2022-04-10 11:41:36", "2022-04-23 11:21:44");
INSERT INTO posts (title, content, user_id, created_at, updated_at) VALUES ("위코드 2일차", "Javascript 기본 문법 학습..", 1, "2022-04-10 11:41:36", "2022-04-23 11:21:54");
INSERT INTO posts (title, content, user_id, created_at, updated_at) VALUES ("위코드 3일차", "웹서비스의 역사와 발전 세션을 듣고..", 1, "2022-04-10 11:41:36", "2022-04-23 11:22:01");
INSERT INTO posts (title, content, user_id, created_at, updated_at) VALUES ("자료구조 1번", "BigO Notation이란 무엇인가?", 2, "2022-04-10 11:41:36", "2022-04-23 11:22:13");
INSERT INTO posts (title, content, user_id, created_at, updated_at) VALUES ("자료구조 2번", "시간 복잡도와 공간 복잡도에 대해서..", 2, "2022-04-22 14:12:45", NULL);
INSERT INTO posts (title, content, user_id, created_at, updated_at) VALUES ("프론트 개발 입문", "프론트 입문 HTML이란 무엇인가?", 3, "2022-04-23 11:43:25", "2022-04-23 11:56:25");

  • likes
ALTER TABLE likes ADD created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP;
INSERT INTO likes (user_id, post_id, created_at) VALUES (1, 3, "2022-07-16 14:18:26");
INSERT INTO likes (user_id, post_id, created_at) VALUES (2, 3, "2022-07-16 14:18:26");
INSERT INTO likes (user_id, post_id, created_at) VALUES (3, 3, "2022-07-16 14:18:26");
INSERT INTO likes (user_id, post_id, created_at) VALUES (4, 3, "2022-07-16 14:18:26");
INSERT INTO likes (user_id, post_id, created_at) VALUES (3, 5, "2022-07-16 14:18:47");
INSERT INTO likes (user_id, post_id, created_at) VALUES (1, 6, "2022-07-16 14:18:47");

  • comments
    (과제에는 없으나 추후 구현 예정?)

2-1. 과제 #1

2-2. 과제 #2

http 통신을 이용하여, 한 명 이상의 유저를 회원가입 해봅니다. Express 를 이용해 가상의 Westagram 서비스에 회원가입을 완료해주세요. 이 때 별도의 모듈화 없이 하나의 파일 (app.js) 에서 모든 코드를 작성합니다.

  • 회원가입 API
    • 알맞은 API 호출 URL을 설정하여서 클라이언트와(httpie/postman) 통신을 성공해주세요.
    • 알맞은 http 메소드를 선정하여서 유저의 정보를 백엔드 서버에 전달해주세요.
    • 데이터가 생성됬을 때에 알맞는 http 상태코드를 반환해주세요.
    • 데이터가 잘 db에 저장되었는지 여부를 테이블 스크린샷을 찍어서 PR에 같이 첨부하여 올려주세요.
    • http response로 반환하는 JSON 데이터의 형태는 다음과 같습니다.
{
  "message" : "userCreated"
}

소스코드

app.post("/users/signup", async(req, res, next) => {
    const { name, email, profileImage, password } = req.body

    await appDataSource.query(
        `INSERT INTO users(
            name, 
            email,
            profile_image,
            password
        ) VALUES (?, ?, ?, ?);
        `, [name, email, profileImage, password]
    );

    res.status(201).json({ message : "userCreated"});
})

httpie로 유저 회원가입 실행하기

postman으로 유저 회원가입 실행하기

mysql 확인하기
select * from users;

2-3. 과제 #3

http 통신을 이용하여, 두 건 이상의 게시글을 등록해봅니다. 이 때 앞서 작성하였던 파일 (app.js) 에서 이어서 코드를 작성합니다.

  • 게시글 등록 API
    알맞은 API 호출 URL을 설정하여서 클라이언트와(httpie/postman) 통신을 성공해주세요.
    • 알맞은 http 메소드를 선정하여서 게시글 내용 및 유저의 id 값을 백엔드 서버에 전달해주세요.
    • 데이터가 생성됬을 때에 알맞는 http 상태코드를 반환해주세요.
    • 데이터가 잘 db에 저장되었는지 여부를 테이블 스크린샷을 찍어서 PR에 같이 첨부하여 올려주세요.
    • http response로 반환하는 JSON 데이터의 형태는 다음과 같습니다.
{
  "message" : "postCreated"
}

소스코드

app.post("/posts/signup", async(req, res, next) => {
    const { title, content, imageUrl, userId } = req.body;

    await appDataSource.query(
        `INSERT INTO posts(
            title,
            content,
            image_url,
            user_id
        ) VALUES ( ?, ?, ?, ? );
        `, [title, content, imageUrl, userId]
    );

    res.status(201).json({ message : "postCreated"});
})

httpie로 게시물 등록 실행하기
http -v POST 127.0.0.1:3000/posts/signup title="test_title1" content="test_content1" imageUrl="https://namu.wiki/jump/gZ1Bu4niCcY0xiLce31zTqT8LpUTWUhyabiB59f4C0FEYW%2BNyojDoZr2PobTX8fn%2BwSmxsXmp1ZOuxLkYk3%2Btw%3D%3D" userId:=4

postman으로 게시물 등록 실행하기

mysql 확인하기

2-4. 과제 #4

http 통신을 이용하여, 게시글 전체 리스트를 불러와봅니다. 이 때 앞서 작성하였던 파일 (app.js) 에서 이어서 코드를 작성합니다.

  • 게시글 리스트 불러오기 API
    • 알맞은 API 호출 URL을 설정하여서 클라이언트와(httpie/postman) 통신을 성공해주세요.
    • 데이터가 호출될 때의 알맞는 http 상태코드를 반환해주세요.
    • http response로 반환하는 데이터의 형태는 다음과 같습니다.
{
    "data" : [
	{
	    "userId"           : 1,
	    "userProfileImage" : "userProfileImage1",
            "postingId"        : 1,
            "postingImageUrl"  : "imageUrlSample1",
	    "postingContent"   : "sampleContent1"
	},
	{  
	    "userId"           : 2,
	    "userProfileImage" : "userProfileImage2",
            "postingId"        : 2,
            "postingImageUrl"  : "imageUrlSample2",
	    "postingContent"   : "sampleContent2"
	},
	{  
	    "userId"           : 3,
	    "userProfileImage" : "userProfileImage3",
            "postingId"        : 3,
            "postingImageUrl"  : "imageUrlSample3",
	    "postingContent"   : "sampleContent3"
	},
	{  
	    "userId"           : 4,
	    "userProfileImage" : "userProfileImage4",
            "postingId"        : 4,
            "postingImageUrl"  : "imageUrlSample4",
	    "postingContent"   : "sampleContent4"
	}
]
}

소스코드

app.get("/posts/lookup", async(req, res, next) => {
    await appDataSource.manager.query(
        `SELECT 
                users.id as userId, 
                users.profile_image as userProfileImage, 
                posts.id as postingId, 
                posts.image_url as postingImageUrl, 
                posts.content as postingContent 
            FROM users 
            INNER JOIN posts 
            ON users.id = posts.user_id
        `, (err, rows) => {
            res.status(200).json({ "data" : rows });
        });
})

httpie로 게시글 전체 리스트 불러오기 실행하기
http -v GET 127.0.0.1:3000/posts/lookup

postman으로 게시글 전체 리스트 불러오기 실행하기

mysql 확인하기

2-5. 과제 #5

http 통신을 이용하여, 한명의 유저가 작성한 게시글을 불러옵니다. 이 때 앞서 작성하였던 파일 (app.js) 에서 이어서 코드를 작성합니다.

  • 특정 유저가 작성한 게시글 상세 API
    • 알맞은 API 호출 URL을 설정하여서 클라이언트와(httpie/postman) 통신을 성공해주세요.
    • 하나의 유저가 올린 여러개의 게시글을 보여주는 상세 페이지용 API를 작성해주세요.
    • 데이터가 호출될 때에 알맞는 http 상태코드를 반환해주세요.
    • Http response로 반환하는 데이터의 형태는 다음과 같습니다.
{
    "data" : {
        "userId"           : 1,
	"userProfileImage" : "userProfileImage1",
	"postings" : [
		{
	            "postingId"        : 1,
	            "postingImageUrl"  : "postingImageUrlSample1",
		    "postingContent"   : "samplePostingContent1"
		},
	        {
	            "postingId"        : 2,
	            "postingImageUrl"  : "postingImageUrlSample2",
		    "postingContent"   : "samplePostingContent2"
		},
		{
	            "postingId"        : 3,
	            "postingImageUrl"  : "postingImageUrlSample3",
		    "postingContent"   : "samplePostingContent3"
		}
	] 
} 

Today I Learn
console.log(rows) 를 실행한 결과, 배열안에 객체들로 이루어져있다는 걸 알았다! postings에는 아래 rows를 입력받아 userId, userProfileImage 라는 키를 삭제하는 함수를 만들고 그 반환값을 대입해주면 될 거 같다!

소스코드

app.get("/users/posts/lookup/:id", async(req, res, next) => {
    const { id } = req.params;
    
    await appDataSource.manager.query(
        `SELECT 
                users.id as userId, 
                users.profile_image as userProfileImage, 
                posts.id as postingId, 
                posts.image_url as postingImageUrl, 
                posts.content as postingContent 
            FROM users 
            INNER JOIN posts 
            ON users.id = posts.user_id 
            WHERE users.id = ${id}; 
        `, 
        (err, rows) => { 
            //console.log(rows);
            let postings
            res.status(200).json(
                { "data" : {
                        "userId" : rows[0].userId, 
                        "userProfileImage" : rows[0].userProfileImage,
                        "postings" : postArr(rows)
                    }
                });
        });
})

userId, userProfileImage 키를 삭제해주는 함수를 선언:

const postArr = rows => {
    for(let i=0; i<rows.length; i++){
        delete rows[i].userId;
        delete rows[i].userProfileImage;
    }
    return rows;
}

httpie로 유저가 작성한 게시글 상세 실행하기 (id=1인 유저)

postman으로 유저가 작성한 게시글 상세 확인하기 (id=1인 유저)

mysql 확인하기 (id=1인 유저)

2-6. 과제 #6

http 통신을 이용하여, 게시글 전체 리스트와 상세 리스트를 불러와봅니다. 이 때 앞서 작성하였던 파일 (app.js) 에서 이어서 코드를 작성합니다.

  • 게시글 정보 수정 엔드포인트 요구 사항
    • 알맞은 API 호출 URL을 설정하여서 클라이언트와(httpie/postman) 통신을 성공해주세요.
    • 데이터베이스에 저장되어있는 1번 id 유저의 1번 id 게시글을 확인합니다. 이후 해당 내역의 content를 기존의 데이터와 다른 내용으로 수정하여 저장합니다.
    • postingId가 1번인 게시물의 내용을 “기존과 다르게 수정한 내용입니다.”로 수정한다면, 데이터베이스에서 끌어와 http response로 반환하는 데이터의 형태는 다음과 같습니다.
{
	"data" : {
	    "userId"           : 1,
	    "userName"         : "weCode",
            "postingId"        : 1,
            "postingTitle"     : "간단한 HTTP API 개발 시작!",
	    "postingContent"   : "기존과 다르게 수정한 내용입니다."
	}
}

소스코드

app.patch("/posts/update/:userId/:postId", async(req, res, next) => {
    const { userId, postId } = req.params;
    const { content } = req.body;
    await appDataSource.manager.query(
        ` UPDATE 
                posts 
            SET content=? 
            WHERE user_id=${userId} and id=${postId};
        `, [content]
    );

    await appDataSource.manager.query(    
        `SELECT 
                users.id as userId, 
                users.name as userName, 
                posts.id as postingId, 
                posts.title as postingTitle, 
                posts.content as postingContent 
            FROM users 
            INNER JOIN posts 
            ON users.id = posts.user_id
            WHERE users.id=${userId} and posts.id=${postId};
        `, (err, rows) => { 
            console.log(rows)
            res.status(200).json({"data" : rows});
    });
})

httpie로 게시글 정보 수정 실행하기

mysql로 게시글 정보 수정 확인하기

Today I Learn
req.params 로 할당한 변수는 쿼리(`${변수명}`) 하면 되고
req.body 로 할당한 변수는 쿼리(`?`, [변수명]) 하면 된다

영상자료에서 manager를 써도 동작되지만 가독성에 안 좋으므로 안 쓰는 방향으로 하라고 들었던 거 같다. 그래도 그냥 되니까 썼다.

2-7. 과제 #7

http 통신을 이용하여, 한 건의 게시글을 삭제해봅니다. 이 때 앞서 작성하였던 파일 (app.js) 에서 이어서 코드를 작성합니다.

  • 게시글 삭제 API
    • 알맞은 API 호출 URL을 설정하여서 클라이언트와(httpie/postman) 통신을 성공해주세요.
    • 데이터가 삭제 될 때에 알맞는 http 상태코드를 반환해주세요.
    • http response로 반환하는 JSON 데이터의 형태는 다음과 같습니다.
{
	"message" : "postingDeleted"
}
// #7 게시글 삭제 엔드포인트 구현
app.delete("/posts/delete/:id", async(req, res, next) => {
    const { id } = req.params;
    await appDataSource.query(
        `DELETE 
            FROM posts
            WHERE id=${id}; 
        `, (err, rows) => {
            res.status(204).end();
        }
    );    
})

또는 상태코드 200일 때;

// #7 게시글 삭제 엔드포인트 구현
app.delete("/posts/delete/:id", async(req, res, next) => {
    const { id } = req.params;
    console.log(req.params);
    await appDataSource.query(
        `DELETE 
            FROM posts
            WHERE id=${id}; 
        `
    );
    res.status(200).json({ message : "postDeleted"});    
})

에러 모음 #10

httpie로 게시글 삭제 확인하기(postId=8)

또는

mysql 게시글 삭제 확인하기

Today I Learn
👉 상태코드 204 를 쓸 경우 res에 body를 담을 수 없어 json() 대신 end()를 쓴다고 한다. 멘토님이 204를 쓰는 것을 추천하셨지만 과제의 {"message" : "postingDeleted"} 를 반환하기 위해 200을 썼다.

2-8. 과제 #8

http 통신을 이용하여, 한 건의 좋아요를 생성해봅니다. 이 때 앞서 작성하였던 파일 (app.js) 에서 이어서 코드를 작성합니다.

  • 게시글 좋아요 누르기 API
    • 알맞은 API 호출 URL을 설정하여서 클라이언트와(httpie/postman) 통신을 성공해주세요.
    • 데이터가 생성 됬을 때 (좋아요가 눌렸을 때의) 알맞는 http 상태코드를 반환해주세요.
    • http response로 반환하는 JSON 데이터의 형태는 다음과 같습니다.
{
	"message" : "likeCreated"
}

소스코드

app.post("/likes/:userId/:postId", async(req, res, next)=>{
    const { userId, postId } = req.params;

    await appDataSource.query(
        `INSERT INTO likes(
            user_id, 
            post_id
        ) VALUES (${userId}, ${postId});
        ` );
    res.status(200).json({ message : "likeCreated" });
})

httpie로 게시글 좋아요 누르기 확인하기(userId=2 가 postId=1에 좋아요 누르기)

postman으로 게시글 좋아요 누르기 확인하기(userId=3 이 postId=1에 좋아요 누르기)

mysql 확인하기

profile
검색하고 기록하며 학습하는 백엔드 개발자

0개의 댓글