12주 3일차 - 네트워크, CRUD, Authentication
1. 배운 것
- useEffect의 종속성 배열 복습
- 서버 api 구현방법 복습
2. 내용 정리
네트워크 체크리스트
Chapter1. TCP/IP
- 패킷교환 방식의 이점에 대해 이해한다.
- IP의 비순서성, 비신뢰성에 대해 이해한다.
- TCP의 3 way handshake 및 그와 비교되는 UDP에 대해 이해한다.
Chapter2. 네트워크 계층 모델
- 네트워크 통신을 계층별로 나눈 이유에 대해 이해한다.
- 통신 과정에서 일어나는 데이터 캡슐화에 대해 이해한다.
Chapter3. HTTP
- HTTP 메세지 구조를 이해한다.
- HTTP의 무상태성과 비연결성에 대해 이해한다.
- HTTP 헤더 중 바디를 설명하는 헤더인 표현헤더에 대해 이해한다.
- HTTP 헤더 중 요청과 응답에서 주로 사용되는 헤더에 대해 이해한다.
- HTTP 헤더 중 서버에 요청하는 컨텐츠를 협상할 수 있는 협상헤더에 대해 이해한다.
Chapter4. Web Cache
- 웹 캐시의 필요성에 대해 이해한다.
- Cache-Control: max-age=60이 어떤 의미인지 이해한다.
- 요청에서 사용하는 If-Modified-Sinse 헤더와 응답에서 사용하는 Last-Modified 헤더에 대해 이해한다.
- 요청에서 사용하는 If-None-Match헤더와 응답에서 사용하는 Etag 헤더에 대해 이해한다.
- 프록시 서버의 필요성에 대해 이해한다.
- 프라이빗 캐시와 프록시 캐시의 차이에 대해 이해한다.
- Cache-Control 헤더의 값인 no-cache와 must-revalidate의 차이에 대해 이해한다.
웹 프론트엔드 개발자가 알아야할 최소한의 백엔드 지식과 코드 (API)
유튜버 라매개발자님의 동일한 제목의 유튜브 강의 영상을 공부하며 정리한 내용입니다.
웹 앱의 동작과정 - 게시판 예시
const express = require('express');
const app = express();
// 더미데이터
const database = [
{ id: 1, title: '글1' },
{ id: 2, title: '글2' },
{ id: 3, title: '글3' },
];
app.get('/', (req, res) => {
// sendFile 말고 render 메서드로도 페이지를 보낼 수 있다. (ejs, pug 검색)
res.sendFile(__dirname + '/views/index.html');
});
app.get('/database', (req, res) => {
res.send(database);
});
app.listen(3000, () => console.log('server on'));
- GET: 서버의 db에 저장된 게시글들을 화면에서 보기 위해 클라이언트가 서버에게 db에 저장된 게시글을 요청하는 api를 호출한다.
- 서버가 클라이언트의 요청을 받아 db에서 데이터를 가져와 클라이언트로 응답을 보낸다.
- POST: 클라이언트가 서버에게 게시글의 제목 또는 내용을 수정해달라는 api를 호출한다.
- 서버는 수정된 내용을 db에 저장하고 수정이 정상적으로 끝났다는 응답을 클라이언트로 보낸다.
- 추가, 삭제 등도 클라이언트가 서버에게 요청하고 서버가 요청을 처리하고 응답을 보낸다.
값 추가 - 방법 1 : req.params (거의 안쓰는 방법이다)
get 요청 응답 코드
// 더미 db
const database = [
{ id: 1, title: '글1' },
{ id: 2, title: '글2' },
{ id: 3, title: '글3' },
];
app.get('/', (req, res) => {
// sendFile 말고 render 메서드로도 페이지를 보낼 수 있다. (ejs, pug 검색)
res.sendFile(__dirname + '/views/index.html');
});
app.get('/database', (req, res) => {
res.send(database); // 응답
});
app.get('/database/:title', (req, res) => {
const title = req.params.title;
database.push({
id: database.length + 1,
title, // title: req.params.title
}); // db에 데이터 추가
res.send('값 추가 성공');
});
값 추가 방법 2 : req.body (주로 쓰이는 방법)
bodyParser 필요.
// bodyParser 사용하기
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
post요청 응답 코드
app.post('/add-database', (req, res) => {
const title = req.body.title;
database.push({
id: database.length + 1,
title, // title: req.body.title
});
res.send('값 추가 성공. 바디파서 사용해서 req.body 받기.');
});
특정한 값만 가져오기(get) - req.params
받아온 req.params는 문자열임에 유의한다.
// * GET: 요청한 특정 값만 보내기
app.get('/database/:id', (req, res) => {
const id = req.params.id; // req.params는 문자열이다.
const data = database.filter(el => el.id === Number(id));
// filter 메서드 대신 find 메서드를 써도 됨.
res.send(data);
});
값 수정하기(PUT)
어떤 글을 수정할지 id로 해당 글을 특정하는 예시.
서버가 클라이언트에게서 수정하고 싶은 값을 받아와야 한다.
// * PUT: 값 수정하기
app.put('/database', (req, res) => {
const id = req.body.id;
const title = req.body.title;
database[id - 1].title = title;
res.send('값 수정 완료.');
});
값 삭제하기(DELETE)
해당하는 id의 데이터를 삭제하는 예시.
// * DELETE: 값 삭제하기
app.delete('/database', (req, res) => {
const id = req.body.id;
database.splice(id - 1, 1);
res.send('값 삭제 완료.');
});
같거나 비슷한 곳의 데이터를 조작할 때 url을 일치시키고 HTTP 메서드를 용도에 맞게 각각 사용하여 RESTful한 api를 만들 수 있다. post, put, delete 모두 url은 /database 로 동일하게 작성하였음.
참고자료
- 웹 프론트엔드 개발자가 알아야할 최소한의 백엔드 지식과 코드 (API) https://www.youtube.com/watch?v=uIWl19relcc&t=1338s
웹 프론트엔드 개발자가 알아야할 최소한의 백엔드 지식과 코드 (user) → 회원가입, 로그인, 에러처리
유튜버 라매개발자님의 동일한 제목의 유튜브 강의 영상을 공부하며 정리한 내용입니다.
1. 회원가입 api 만들기
const database = [{ id: 1, username: 'abc', password: 'abc' }];
// * 회원가입 api 만들기
app.post('/signup', (req, res) => {
const { username, password, age, birthday } = req.body;
database.push({
username,
password,
age,
birthday,
});
res.send('회원가입 성공');
});
로그인 api 만들기
유저가 입력한 값으로 db를 filter하였을때 해당 정보가 없다면 length가 0이고 아이디는 일치하지만 비밀번호가 틀릴 경우도 입력값과 db에 저장된 값과 비교하여 조건분기 할 수 있다. if문 둘 다 해당되지 않으면 마지막에 응답으로 로그인 성공을 보낸다.
// * 로그인 api 만들기
app.post('/login', (req, res) => {
const { username, password } = req.body;
const user = database.filter(user => user.username === username);
if (user.length === 0) {
return res.send('등록되지 않은 id입니다.');
}
if (user[0].password !== password) {
return res.send('비밀번호가 틀립니다.');
}
res.send('로그인 성공.');
});
주의 : 유저의 비밀번호를 평문으로 저장하면 안된다. 반드시 암호화하여 저장해야 한다.
비밀번호 암호화하기
argon2 라이브러리로 간단하게 비밀번호를 해싱하여 암호화를 적용해줄 수 있다.
// * 회원가입 api 만들기 + 비밀번호 암호화 적용하기
app.post('/signup', async (req, res) => {
const { username, password, age, birthday } = req.body;
const hash = await argon2.hash(password);
database.push({
username,
password: hash,
age,
birthday,
});
res.send('회원가입 성공');
});
// 데이터베이스에 비밀번호가 암호화되어 저장된걸 확인할 수 있다.
/*
{
"username": "abcd",
"password": "$argon2id$v=19$m=4096,t=3,p=1$VFzuRA7IxXQF2mV8xsX5jA$OtDJ+M/I+URmCfND6jCBdkN0bHPfUNmTl4NQVet84jc",
"age": "10",
"birthday": "12.11"
}
*/
암호화된 비밀번호와 일치 여부를 로그인시 검증하기
argon2 라이브러리 사용 시 verify 메서드로 검증 가능.
id나 비밀번호가 일치하지 않을 경우 응답의 상태코드는 403번으로 보냄.
// * 로그인 api 만들기 + 비밀번호 검증
app.post('/login', async (req, res) => {
const { username, password } = req.body;
const user = database.filter(user => user.username === username);
if (user.length === 0) {
return res.status(403).send('등록되지 않은 사용자입니다.');
}
if (!(await argon2.verify(user[0].password, password))) {
return res.status(403).send('비밀번호가 틀립니다.');
}
res.send('로그인 성공.');
});
useEffect의 dependency array 복습 - 조건부 effect 발생
의존성 배열이 생략된 경우, 빈 배열인 경우, 종속성 값이 의존성 배열에 존재하는 경우에 따라 콜백함수가 언제 실행되는지 알고 있어야 한다.
- 의존성 배열 생략: 모든 렌더링(컴포넌트 최초 생성, state 변경) 발생 시 콜백함수 실행
- 빈 배열: 최초 렌더링 시 1회만 콜백함수 실행 예시: → 최초 외부 API의 리소스를 받아오고 더 이상 API 호출이 필요하지 않을 경우 사용.
- 종속성 요소 존재: 해당 요소의 값이 변할 때 콜백함수 실행
Create: 게시글 생성하기, useNavigate
판다코딩님 인프런 MERN스택 강의 내용으로 공부 내용을 정리한 부분입니다.
게시판 업로드 폼 같은 경우 제목, 본문 등 다양한 유저의 인풋값 마다 핸들러 함수를 정의할 필요 없이 onChange 이벤트의 콜백함수를 익명함수로 정의하면서 각각의 내용에 맞는 state 변경함수를 내부에서 실행시키면 된다.
클라이언트 - Upload.js (글 작성 페이지)
const Upload = props => {
// useNavigate 사용
let navigate = useNavigate();
const [title, setTitle] = useState('');
const [content, setContent] = useState('');
const onSubmit = e => {
e.preventDefault();
if (title === '' || content === '') return alert('모든 항목을 채워주세요');
let body = {
title,
content,
};
axios
.post('/api/post/submit', body)
.then(response => {
if (response.data.success) {
alert('포스팅 성공! 홈으로 돌아갑니다.(useNavigate)');
navigate('/');
} else {
return alert('포스팅 실패');
}
})
.catch(err => console.log(err));
};
- 제목, 내용의 state를 각각 생성.
- 입력창에서 onChange 이벤트 발생 시의 핸들러함수를 익명함수로 정의하면서 제목, 내용을 입력값(e.currentTarget.value)으로 setState.
- axios import 하여 제출버튼 클릭시 실행되는 핸들러함수에서 post 요청 보내기.
- db로 보낼 데이터인 새 객체를 생성하고 내용은 제목과 내용의 state로.
- axios.post 하면서 url, 객체 2가지를 인수로 보내고
- 이어받아서 프로미스 체이닝(1. then: 성공시 동작, 2. catch: 에러 핸들링) 한다.
- 제출버튼 클릭시 핸들러함수에서 preventDefault 시킬 것. 작성한 axios 코드가 실행되지 않고 페이지가 새로고침되는 것을 방지해야 함.
- react-router-dom의 useNavigate를 사용하여 글 업로드 완료 시 홈 디렉토리( ‘ / ’ )로 이동시키기.
서버 - index.js
app.post('/api/post/submit', (req, res) => {
let temp = req.body;
const communityPost = new Post(temp);
// .save() 없으면 db로 데이터 안넘어옴!!
communityPost
.save()
.then(() => {
res.status(200).json({ success: true, text: 'hi' });
})
.catch(err => {
res.status(400).json({ success: false });
});
});
post 요청시의 경로를 클라이언트의 Upload.js에서 post요청한 경로와 같게 수정하였다.
더 공부할 내용
- 인증
3. 리뷰
3.1 좋았던 점
3.2 아쉬웠던 점
- 인증 공부하려고 한건데 지난 과정들에서 기억이 나지 않는 부분들과 인증 이전에 알아야 할 것들만 보다가 하루가 다 가버렸다.
- 유어클래스 이론 내용으로만 가득찬 날들은 도저히 내용이 머리에 들어오지가 않는다.
- 들어는 봤고 나중에 보충하잔 식으론 난 공부 못하겠는데.
3.3 notes
보이는 라디오는 회고날 같은때나 좀 집어넣었으면 좋겠다. 취지야 좋은데 선참시든 그룹 회고든 이런 세션들은 좀 뻔히 다음 과제가 미친듯이 어려울게 보이는데 흐름 끊는 위치에 넣는건 좀 아닌것 같다.
'기록 > 코드스테이츠 프론트엔드' 카테고리의 다른 글
13주차 - 반응형 웹 제출 과제 (0) | 2022.07.22 |
---|---|
12주 4일차 - Auth Basic (0) | 2022.07.15 |
12주 2일차 - 웹 접근성, 서버 복습 (0) | 2022.07.12 |
12주 1일차 - 데일리코딩 뜯어보기 (0) | 2022.07.11 |
11주차 마무리 - 힐링 (0) | 2022.07.10 |