8주 1일차 - HTTP/네트워크 실습
1. 배운 것
- rest api
- 모던 자바스크립트 딥 다이브 16장
- 유데미 React 완벽 가이드 by Maximilian Schwarzmüller
- fetch api로 GET 요청 보내기
- async/await 실사용
- try-catch문 에러 핸들링
2. 내용 정리
종합 퀴즈
- 1번 : REST 원칙을 준수하지 않은 GET 요청 수정하기
- PUT 메서드는 서버에 요청 메시지의 본문을 저장해야 할 경우에 사용한다.
- root-endpoint( root-URL ): 클라이언트가 API로 서버에 요청 시 서버가 요청을 수락하는 시작점.
- 엔드포인트에는 행위에 대한 명사가 아닌 리소스를 지칭하는 명사를 사용한다.
(inquiry:문의) - 요청 메세지이기 때문에 응답에 Location 헤더 정보의 전달 여부는 알 수 없다.
- 3번 : 무한 스크롤의 엔드포인트로 적절한 코드
- 정답:
GET /tweets?offset=10&limit=10
- 기존의
GET /tweets
엔드포인트 재사용이 적절함. - 페이지네이션으로 트윗 목록을 끊어 가져온다.
- query parameter의 컨벤션: offset, limit
- 정답:
- 4번 : ‘특정 위치’ 기반의 모든 식당 목록을 ‘조회’ 하고 특정 메뉴만 ‘필터링’ 하는 엔드포인트
- 정답:
GET /restaurants?coordinate=126.9178889,37.5561619&type=korean
- 엔드포인트만 보고도 응답 내용을 예측가능하게 리소스를 지칭하는 단어 restaurants 사용.
- 좌표(coordinate)를 query parameter로 제공.
- 한식만 필터링 하기 위해 필터링 조건을 query parameter로 전달.(type=korean)
- 정답:
- 5번 : GET 요청에는 메시지의 body가 없기 때문에 query parameter를 사용하여 구현해야 한다.
- 7번 : REST 원칙에 알맞게 응답 메시지 수정하기
- POST 메서드로 새로운 예약 정보를 ‘생성' 해야 한다. (c in crud)
입력 폼의 새로운 입력값 전송은 POST메서드로 처리한다.
PUT은 update. (u in crud) - 200 : success → 메소드에 따라 성공의 의미가 달라진다. 참고
- 201: created → 요청이 성공하여 그에 따라 새로운 리소스 생성 완료.
- 409: conflict → 요청이 현재 서버의 상태와 충돌될 때.
- 503 Service Unavailable → 서버 중단, 과부하의 경우.
- POST 메서드로 새로운 예약 정보를 ‘생성' 해야 한다. (c in crud)
- 8번 : 게시물을 삭제하는 엔드포인트 작성
- 메서드를 다시 엔드포인트에 명시할 필요 없음.
- 어떤 리소스를 삭제하는지 명시할 것. (몇번째 게시글인지)
실시간 세션
프론트여도 api문서 보고 갖다 쓸 수 있을만큼은 알아야 함.
영어 공부 많이 하는게 좋다. 공식문서 많이 읽어야 한다.
- constrain : 제약하다
- language-agnostic : 언어에 상관없는
- HTTP 메시지
- body === payload
- body를 요청에 대한 응답으로 보낼 수 있다.
REST(Representational State Transfer)란 자원(resource)에 대한 주소를 정하는 방법, 규칙을 말한다.
프로젝트때 REST 원칙이 잘 안지켜진 경우 많이 보았음. 개별 리소스와 통신해야 한다.
- 어떤 응답이 제공되는가
- 어떤 상태를 변화시키는가
- 동사 사용보다 리소스를 지칭하는 명사 사용을 권장.
- POST /click 보다 POST /seat/b10 이 훨씬 맞는 방식.
- 목적에 맞는 메서드를 사용해야 한다.
query parameter와 path parameter의 차이
- path parameter는 하나의 고유한 값을 받기 위해 사용(한개).
GET /people/49
- GET은 body 대신 query parameter를 사용하여 필터링한다(여러개).
GET /39th?age=25
유닛10 키워드: express, http transaction, if문에서 조건으로 메서드의 종류
POST 메서드의 응답 상태코드는 201을 쓰는 게 맞다.
실무에서 postman 굉장히 많이 쓴다.
postman에서 코드스니펫으로 요청 메세지 자바스크립트로 받아서 써볼 수 있다.
postman에서 dynamic variables : 변수로 값 지정 가능.
리액트 - HTTP 요청하기
절대로 리액트 앱과 데이터베이스를 직접 연결해선 안된다.
개발자 도구로 모든 코드가 보여지므로 데이터베이스 접근을 허가하는 인증 정보가 노출되는 등 보안 문제와 성능상의 단점이 생긴다. (Database credentials exposed in the browser)
따라서 클라이언트와 별도로 백엔드 애플리케이션을 사용하여 별도의 서버나 데이터베이스와 같은 서버에서 운용하여 보안 설정과 같은 세부사항을 클라이언트와 따로 관리한다.
REST vs graphQL
HTTP 요청에 관한 api를 말할 때 주로 거론되는 것이 REST api와 graphQL api이다.
서버가 데이터를 노출하는 방식에 대한 서로 다른 두 표준들이다.
api에 정의된 형식에 맞춰서 클라이언트에서 각각 요청에 맞는 url로 요청하면 요청한 값에 맞는 데이터를 서버가 전달한다. 즉 엔트리포인트가 달라지면 요청도 달라지고 응답도 그에 맞게 달라진다.
fetch API를 이용한 REST API 호출
추후에는 axios로 하겠지만 우선 fetch api로 http 요청하는 법을 먼저 공부한다.
fetch
메서드는 자바스크립트 빌트인 메서드이다. ie에선 fetch api 미지원.
백엔드로 GET 요청 보내기
- GET 요청은 두번째 인수 전달 필요 x. 따라서 경로만 전달.
- response 객체의 내장 메서드인 .json()을 사용하여 json 형식인 데이터의 response 본문을 자바스크립트로 변환시킨다.
- fetch 메서드의 then 후속 처리 메서드의 리턴값인 자바스크립트로 변환된 json 데이터를 다시 then 후속 처리 메서드로 해당 데이터의 원하는 값을 state에 담는다.
import React, { useState } from 'react';
import MoviesList from './components/MoviesList';
import './App.css';
function App() {
const [movies, setMovies] = useState([]);
function fetchMoviesHandler() {
// get 요청은 두번째 인수 전달 필요 x. 따라서 fetch 메서드에 경로만 전달.
// fetch 메서드는 promise 객체를 반환한다. 따라서 후속 처리 메서드 사용.
fetch('https://swapi.dev/api/films/')
.then(response => {
return response.json();
})
.then(data => {
const transFormedMovies = data.results.map(movieData => {
return {
id: movieData.episode_id,
title: movieData.title,
openingText: movieData.opening_crawl,
releaseDate: movieData.release_date,
};
/**
* 자바스크립트로 변환된 json 데이터 파일에서 원하는 값만 뽑기 위해
* map 메서드를 사용하여 json의 프로퍼티 키를 우리 파일에 맞게 변경하고
* 새 객체를 생성하여 원하는 값만 받아온다.
* */
});
setMovies(transFormedMovies);
});
}
return (
<React.Fragment>
<section>
<button onClick={fetchMoviesHandler}>Fetch Movies</button>
</section>
<section>
<MoviesList movies={movies} />
</section>
</React.Fragment>
);
}
export default App;
비동기/ 대기 사용하기
async 함수와 await 키워드를 사용하여 then 메서드 체이닝을 대체하고 코드를 단순화 시켰다.
await 키워드를 사용하여 비동기 처리 순서를 순차적으로 처리되게 하였다.
- 순서
- fetch 하고 결과를 response 변수에 담음.
- json객체를 자바스크립트로 변환(
response.json();
)하고 결과를 data 변수에 담음.
function App() {
const [movies, setMovies] = useState([]);
async function fetchMoviesHandler() {
const response = await fetch('https://swapi.dev/api/films/');
const data = await response.json();
const transFormedMovies = data.results.map(movieData => {
return {
id: movieData.episode_id,
title: movieData.title,
openingText: movieData.opening_crawl,
releaseDate: movieData.release_date,
};
/**
* 자바스크립트로 변환된 json 데이터 파일에서 원하는 값만 뽑기 위해
* map 메서드를 사용하여 json의 프로퍼티 키를 우리 파일에 맞게 변경하고
* 새 객체를 생성하여 원하는 값만 받아온다.
* */
});
setMovies(transFormedMovies);
}
로딩 및 데이터 state 처리하기
function App() {
// 1. api에서 원하는 값만 뽑아서 새 목록으로 담기 위한 state
const [movies, setMovies] = useState([]);
// 2. 로딩 여부 state
// 컴포넌트 렌더링 시에는 영화 데이터가 아직 fetch되지 않았으므로 false, 버튼 클릭시 로딩되므로 true
const [isLoading, setIsLoading] = useState(false);
async function fetchMoviesHandler() {
setIsLoading(true); // 버튼 클릭시 로딩 시작이므로 false -> true
// get 요청은 두번째 인수 전달 필요 x. 따라서 fetch 메서드에 경로만 전달.
// fetch 메서드는 promise 객체를 반환한다. 따라서 후속 처리 메서드 사용.
// then 체이닝을 async/await으로 코드 단순화 시킴.
const response = await fetch('https://swapi.dev/api/films/');
const data = await response.json();
const transFormedMovies = data.results.map(movieData => {
return {
id: movieData.episode_id,
title: movieData.title,
openingText: movieData.opening_crawl,
releaseDate: movieData.release_date,
};
/**
* 자바스크립트로 변환된 json 데이터 파일에서 원하는 값만 뽑기 위해
* map 메서드를 사용하여 json의 프로퍼티 키를 우리 파일에 맞게 변경하고
* 새 객체를 생성하여 원하는 값만 받아온다.
* */
});
setMovies(transFormedMovies);
setIsLoading(false); // 데이터를 다 불러왔으니 로딩 종료
}
return (
<React.Fragment>
<section>
<button onClick={fetchMoviesHandler}>Fetch Movies</button>
</section>
<section>
{!isLoading && movies.length > 0 && <MoviesList movies={movies} />}
{/* 1. 로딩 완료 && 영화 목록이 있는 경우 */}
{!isLoading && movies.length > 0 && <MoviesList movies={movies} />}
{/* 2. 로딩 완료 && 영화 목록이 없는 경우 */}
{!isLoading && movies.length === 0 && <p>Found no movies.</p>}
{/* 3. 로딩 진행중 */}
{isLoading && <p>Loading movie datas...</p>}{isLoading && <p>Loading movie datas...</p>}
</React.Fragment>
);
}
리액트에서 단축평가 이용하기
- &&(AND) : 전부 true면 마지막 true가 반환된다.
- ||(OR) : 첫번째 true가 반환된다.
{/* 1. 로딩 완료 && 영화 목록이 있는 경우 */}
{!isLoading && movies.length > 0 && <MoviesList movies={movies} />}
{/* 2. 로딩 완료 && 영화 목록이 없는 경우 */}
{!isLoading && movies.length === 0 && <p>Found no movies.</p>}
{/* 3. 로딩 진행중 */}
{isLoading && <p>Loading movie datas...</p>}
HTTP 오류 처리하기 - state 사용
- then 메서드 사용시 에러 핸들링: catch 메서드
- async/await 사용시 에러 핸들링: try-catch문
// 잘못된 경로로 fetch 시도
const response = await fetch('https://swapi.dev/api/films/ㅁㄴㅇㄹ');
// Failed to load resource: the server responded with a status of 404 ()
잘못된 경로로 접근할 경우 동작할 내용을 만들기 위해 에러 상태를 담을 state가 필요하다.
fetch api는 없는 데이터에 어떤 작업을 진행하려고 할 경우에만 에러로 throw 한다. 400번대 에러가 발생해도 에러를 표시하지 않는다. (axios는 에러 상태 코드에 맞는 에러를 throw 해줌.)
따라서 fetch api를 사용중이라면 에러를 직접 구현해주어야 함.
function App() {
// 1. api에서 원하는 값만 뽑아서 새 목록으로 담기 위한 state
const [movies, setMovies] = useState([]);
// 2. 로딩 여부 state
// 컴포넌트 렌더링 시에는 영화 데이터가 아직 fetch되지 않았으므로 false, 버튼 클릭시 로딩되므로 true
const [isLoading, setIsLoading] = useState(false);
// 3. 에러 여부 state
const [error, setError] = useState(null);
async function fetchMoviesHandler() {
setIsLoading(true); // 버튼 클릭시 로딩 시작이므로 false -> true
setError(null); // 이전에 에러를 받았을 경우 초기화
// get 요청은 두번째 인수 전달 필요 x. 따라서 fetch 메서드에 경로만 전달.
// fetch 메서드는 promise 객체를 반환한다. 따라서 후속 처리 메서드 사용.
// then 체이닝을 async/await으로 코드 단순화 시킴.
try {
const response = await fetch('https://swapi.dev/api/films/');
// fetch api 에러 처리 구현은 파싱 전에 해야 한다. 따라서 if문을 파싱 전에 위치시킴.
if (!response.ok) {
throw new Error('이런! 무언가 잘못되었네요.');
}
const data = await response.json();
// 상단 if문 실행 시(에러 발생) 아래 코드들은 실행되지 않음.
const transFormedMovies = data.results.map(movieData => {
return {
id: movieData.episode_id,
title: movieData.title,
openingText: movieData.opening_crawl,
releaseDate: movieData.release_date,
};
/**
* 자바스크립트로 변환된 json 데이터 파일에서 원하는 값만 뽑기 위해
* map 메서드를 사용하여 json의 프로퍼티 키를 우리 파일에 맞게 변경하고
* 새 객체를 생성하여 원하는 값만 받아온다.
* */
});
setMovies(transFormedMovies);
} catch (error) {
setError(error.message);
}
setIsLoading(false); // 데이터를 다 불러왔거나 에러 발생 시 로딩 종료.
}
return (
<React.Fragment>
<section>
<button onClick={fetchMoviesHandler}>Fetch Movies</button>
</section>
<section>
{/* 1. 로딩 완료 && 영화 목록이 있는 경우 */}
{!isLoading && movies.length > 0 && <MoviesList movies={movies} />}
{/* 2. 로딩 완료 && 에러 없음 && 영화 목록이 없는 경우 */}
{!isLoading && movies.length === 0 && !error && <p>Found no movies.</p>}
{/* 3. 로딩 진행중 */}
{isLoading && <p>Loading movie datas...</p>}
{/* 4. 에러 발생시 - 로딩 완료 && 에러메세지 있음 */}
{!isLoading && error && <p>{error}</p>}
</section>
</React.Fragment>
);
}
에러 관련 제어문 코드 변수로 정리하기
if (movies.length > 0) {
content = <MoviesList movies={movies} />;
}
if (error) {
content = <p>{error}</p>;
}
if (isLoading) {
content = <p>Loading movie datas...</p>;
}
return (
<React.Fragment>
<section>
<button onClick={fetchMoviesHandler}>Fetch Movies</button>
</section>
<section>{content}</section>
</React.Fragment>
);
역방향 데이터 흐름 - 자식 → 부모
부모의 state 변경함수를 자식이 props로 받아 부모의 state를 변경시킬 수 있다.
function ParentComponent() {
const [value, setValue] = useState('부모의 원래 제목');
const handleChange = () => setValue('자식이 바꾼 부모의 제목');
return (
<div>
<h1>{value}</h1>
<ChildComponent handleButtonClick={handleChange} />
</div>
);
}
function ChildComponent({ handleButtonClick }) {
return (
<div>
<button onClick={handleButtonClick}>자식이 부모 제목 바꾸기</button>
</div>
);
}
참고 자료
자바스크립트 단축 평가
서론 리액트 프로트를 진행하다보면 위와같은 코드를 심심치 않게 쓴다. 나는 보통 url 을 통해 데이터를 fetching 하였을때 비동기 처리를 위해 데이터가 있을때만 해당 코드가 동작하게 하는 이
hwani.dev
3. 리뷰
3.1 좋았던 점
- 오랜만에 친구랑 통화해서 좋았다.
3.2 아쉬웠던 점
- useEffect 언제 해?
3.3 notes
미리미리 여러 취업포털에서 프론트엔드 모집 공고를 찾아보며 내가 선호하는 회사와 요구조건을 확인해보면서 해당 기술들과 회사의 업무와 연관이 있는 개인/팀 프로젝트를 미리 구상해두고 습작을 많이 연습해보는게 좋겠다.
머리로 이해하고 넘기는게 아니라 그날 그날 배운 기술들을 실제로 백지상태에서 습작으로 연습해보며 써먹어 봐야 제대로 복습할수 있는데 지금 방향성이 안 잡힌 상태에서인지 잘 안되고 있어서 방향성을 잡기에 좋은 방법이라고 생각한다.
지난주보다 많이 힘 뺐다. 물론 과정에 최선은 다 해야 하고 자기합리화인지 정말 쉬어야 할 때인지는 구분해야겠지만 강박관념을 조금은 내려놓은 것 같다. 평일엔 어제와 오늘, 내일의 학습내용만 생각하자. 그 범위를 벗어나는 건 주말에 생각하는 시간을 정해두고 생각하자. 걱정도 걱정시간을 정해두고 걱정하자.
4. 할 일
- The response also has a “status” field which holds the concrete response status code.
You could also manually check that. (fetch api 에러 핸들링 부분)
'기록 > 코드스테이츠 프론트엔드' 카테고리의 다른 글
8주 3일차 - StatesAirline Client (2) | 2022.06.16 |
---|---|
8주 2일차 - Effect Hook (0) | 2022.06.14 |
7주차 일요일 - react router dom, useParams (0) | 2022.06.12 |
7주차 토요일 - 리액트 복습 (0) | 2022.06.11 |
7주 5일차 - REST API, 타입스크립트, 이진 탐색 (0) | 2022.06.10 |