2023 July TIL
30 minutes per day
- 7/1: React-Query
- 7/2: Recoil
- 7/3: IT Terms
- 7/4: Rest
- 7/6: Error Boundary
- 7/7: Rest Api
- 7/8: Test with Jest
- 7/17: React-Query II
- 7/18: React-Query III
- 7/19: OOP Revisited
- 7/20: BFS, DFS
- 7/22: null vs undefined
- 7/25: Framework and Library
7/1: React-Query
react-query
는 서버의 값을 클라이언트에 가져오거나, 캐싱, 값 업데이트, 에러핸들링 등 비동기 과정을 더욱 편하게 하는데 사용됩니다.- \(\Rightarrow\) 서버, 클라이언트 데이터 분리
- React Query 장점:
- 캐싱
GET
데이터에 대해 update 시 자동으로GET
다시 수행- 데이터가 오래되었다고 판단되면 다시
GET
(invalidateQueries
) - 동일 데이터 여러번 요청들어오면 한번만 요청함 (옵션에 따라 중복 호출 허용 시간 조절 가능)
- 무한 스크롤 (??)
- 비동기 과정을 선언적으로 관리 가능 (React Suspense 공부하기..)
- React Hook와 유사
이렇게 보면 사용 안할 이유가 없어보인다. 단점들도 찾아봐야겠다.
useQuery
데이터를
GET
하기 위해 사용
useQuery(uniqueKey, api_call);
unique Key
: (string \(\mid\) 배열) 다른 컴포에서 해당 키를 부르면 호출 가능- (배열일 경우) 배열[0]: string, 배열[1]: query 함수 내부 parameter로 들어가 변수처럼 사용 가능
api_call
: 비동기 호출 함수, Promise가 들어가야 함return
: api의 성공여부 또는 API return값을 포함한 객체
const Members = () => {
const { isLoading, isError, data, error } = useQuery("members", fetchMemberList, {
refetchOnWindowFocus: false,
retry: 0,
onSuccess: data => console.log(data)
onError: e => console.log(e.message); // API 호출 실패
});
if (isLoading) return <span>Loading...</span>
if (isError) return <span>Error: {error.message}</span>
return (
<ul>
{data.map(member => (
<li key={member.id}>{member.name}</li>
))}
</ul>
);
};
refetchOnWindowFocus
: 사용자가 다른 창 갔다오면 함수 재실행 여부retry
: 실패 시 재호출 횟수onError
: HTTP에러가 아닌, 정말 api 호출이 실패한 경우만 호출 (강제로 에러 발생시키려면 api단에서 throw Error 날리기) 참고isLoading, isError
말고 status로 한번에 처리 가능
const { status, data, error } = useQuery("members", fetchMemberList);
if ( status === 'loading' ) ...
if ( status === 'error' )...
useQuery
는 비동기로 작동 \(\Rightarrow\) 컴포 내에 여러개의useQuery
존재 시 한꺼번에 실행됨 \(\rightarrow\)useQueries
추천enabled
사용 시 동기적으로 사용 가능! (3rd parameter ofuseQuery
)
const { data: memberList, error, isFetching } = useQuery("members", fetchMemberList);
const { data: nextMember, error, isFetching } = useQuery("nextMembers",
fetchNextMemberList, {
enabled: !!memberList, // true가 되면 fetchNextMemberList 실행
});
useQueries
- 여러 비동기적으로 실행되는
useQuery
에 대해 모든 변수, 로딩, 성공, 실패처리를 해주어야 함 useQueries
:promise.all
처럼useQuery
를 하나로 묶기
const queries = useQueries([
{ queryKey: "members", queryFn: fetchMembers },
{ queryKey: "conferences", queryFn: fetchConferences },
]);
useEffect(() => {
console.log(result);
const loadingFinishAll = result.some((result) => result.isLoading);
console.log(loadingFinishAll); // loadingFinishAll이 false이면 최종 완료
}, [result]);
uniqueKey 활용
- 배열에 unique key: query 함수 내부에서 변수로 사용 가능 (parameter로 들어감)
const clubId = 1;
const result = useQueries([
{
queryKey: ["getMembers", clubId],
queryFn: (params) => {
console.log(params); // {queryKey: ['getMembers', 1 ], pageParam: undefined, meta: undefined}
return api.getMembersInfo(clubId);
},
},
{
queryKey: ["getConference", clubId],
queryFn: () => api.getConferenceInfo(clubId),
},
]);
QueryCache
Query에 대해 성공, 실패 전처리
const queryClient = new QueryClient({
queryCache: new QueryCache({
onError: (error, query) => {
console.log(error, query);
if (query.state.data !== undefined) {
toast.error(`에러가 났어요!!: ${error.message}`);
},
},
onSuccess: data => {
console.log(data)
}
})
});
- 조금 더 공부할것…
useMutation
POST
,UPDATE
용
- return:
useQuery
와 같음
const [id, setId] = useState("");
const [password, setPassword] = useState("");
const loginMutation = useMutation(loginApi, {
onMutate: variable => {
console.log("onMutate", variable); // variable : {loginId: 'xxx', password; 'xxx'}
},
onError: (error, variable, context) => {...} ///error
onSuccess: (data, variables, context) => {
console.log("success", data, variables, context);
},
onSettled: () => {
console.log("end");
}
});
const handleSubmit = () => {
loginMutation.mutate({ loginId: id, password });
};
- Update 후 GET 재실행: mutation 함수 성공 시 unique key로 매핑된 GET함수를
invalidateQueries 에 넣어주기const mutation = useMutation(postMember, { onSuccess: () => { // postMember이 성공하면 members로 맵핑된 useQuery api 함수를 실행합니다. queryClient.invalidateQueries("members"); } });
mutation에서 return 된 값 이용해서 GET함수 params 바꿔주어야 할 경우:
setQueryData const queryClient = useQueryClient(); const mutation = useMutation(editMember, { onSuccess: data => { queryClient.setQueryData(["member", { id: 5 }], data); // data가 fetchMemberById 들어간다 } }); const { status, data, error } = useQuery(["member", { id: 5 }], fetchTodoById); mutation.mutate({ id: 5, name: "TEST1" });
- 더 자세한 내용은: 노경환 깃헙
7/2: Recoil
- KLUB 프로젝트 팀장님이 상태관리 라이브러리로 redux가 아닌 recoil 도입해 보자고 하셔서 급하게 공부!
RecoilRoot
- Facebook에서 개발함
npm i recoil
oryarn add recoil
- Recoil 상태를 활용하는 컴포넌트는 부모 컴포넌트 어딘가에
RecoilRoot
필요!! \(\rightarrow\) root component에 추천
import React from 'react';
import { RecoilRoot, atom, selector, useRecoilState, useRecoilValue } from 'recoil';
function App() {
return (
<RecoilRoot>
<MyApp />
</RecoilRoot>
);
}
Atom
ATOM: piece of state, 어느 컴포넌트에서든 읽기/작성 가능
- 해당 ATOM을 읽는 컴포넌트는 암무적으로 종속됨 \(\rightarrow\) ATOM이 업데이트 될 때 관련된 모든 컴포넌트가 리렌더링 됨
const textState = atom({
key: 'textState', // unique ID
default: '', // 초기값
});
- 읽기 + 쓰기 Atom 사용 시:
useRecoilState() 사용
const [text, setText] = useRecoilState(textState);
Selector
- piece of derived state (state의 Transformation, 즉 변형된 값)
const charCountState = selector({
key: 'charCountState',
get: ({get}) => {
const text = get(textState);
return text.length;
},
});
- selector 값 읽을 때는
useRecoilValue hook 사용
const count = useRecoilValue(charCountState);
Recoil vs Redux
\(Recoil\) | \(Redux\) | |
---|---|---|
장점 | - React의 useState 와 유사, 직관적이며 간단한 구조 - 코드의 양의 줄어듬 | - 가장 익숙한 상태관리 라이브러리 -검증된, 신뢰성 있음 - Redux Devtools 로 상태값이 많아지면 보다 쉬운 디버깅 가능 |
단점 | - \(Redux\)처럼 따로 안정적인 Devtool 존재X. - snapshot 이라는 개념 존재하지만 콘솔을 이용해 봐야 한다- Recoilize라는게 있는데 작은 버그들때문에 실제 사용자들은 콘솔을 선호하신다고 한다…! | - 작은 상태 하나 변경하려 해도 actions , reducer , type 등 보일러 플레이트 코드를 많이 작성해야 함 |
비동기처리 | - Selector 사용, 기본적으로 값을 캐싱 - 한번 들어왔었던 값을 기억하고 있음 \(\rightarrow\) 같은 응답을 보내는 API CALL에 대해서는 추가요청X (성능적으로 좋음) redux 처럼 따로 미들웨어 설치 필요X (내장되어 있음) | redux-thunk , redux-saga , redux-toolkit 등일 이용해 비동기 or 부가기능을 처리함 (검증된 미들웨어) |
- 결론: 디버그의 측면에서는 redux가 더 유리하나, redux는 작성해야 하는 코드 양이 많아진다
Jotai, Zustrand
- recoil은 (2022년 1월 기준) 매우 느리게 발전중, Jotai와 Zustand는 활발한 업데이트 이루어지는 중
- Recoil:
JS
, Jotai & Zustand: 100%TS
JOTAI 공식문서
- Context의 리렌더링 문제를 해결하기 위해 만들어진 React 특화 상태관리 라이브러리
selectAtom
,splitAtom
- Recoil에 영감을 받아, atomic한 상태관리 방식으로 구성 (bottom-up 방식)
- Recoil과 다르게
key
불필요 (보일러 플레이트 코드 줄어듦) - 보일러 플레이트 코드가 redux에 비하면 현저하게 줄어든다.
- 앞으로 React 의 주요 feature일 Suspense(Concurrent mode)를 적용하는데에 적합하게 설계되었다.
모르는 내용, 공부 필요 - 강조점:
- PRIMITIVE: React의
useState
와 유사한 interface - FLEXIBLEE:
atom
들끼리 서로 결합 및 상태에 관여 가능, 타 라이브러리들과 원활한 결합 지원
- PRIMITIVE: React의
ZUSTAND 공식문서
- 한번 사용해 봤는데, 굉장히 간단하게 상태관리 구성이 가능하다.
공식문서도 굉장히 짧다.
- 참고자료: Recoil 공식문서, Neul_bo.log, 개발자 아저씨들 힘을모아
7/3: IT Terms
- 최근에 자주 보인 단어들 복습 및 정리
Bottleneck Effect
- 병목현상
- 병의 목 부분처럼 넓은 길이 갑자기 좁아짐 \(\Rightarrow\) 교통 정체 현상
- 컴퓨터 성능 저하: 엄청난 양의 데이터를 순식간에 내보냄 BUT 메모리가 제대로 소화 불가
Adhoc
- 애드혹: “이것을 위해”, “특별한 목적을 위해서”
- \(\Rightarrow\) Specific-purpose
- 특정한 문제나 일을 위해 만들어진 관습적인 해결책
- 일반화할 수 없는 해결책
- 어떤 다른 목적에 적응시킬 수 없는 해결책
- Ad-hoc Programming: 코드를 특정 목적을 달성하기 위해서만 작성
Ad-hoc Database Queries: 하드코딩
SELECT * FROM employee WHERE id=101; # Adhoc SELECT * FROM employee WHERE id=@id; # params
- Ad-hoc Coding: 설계에 맞춰 개발하는 것이 아닌, 문제 분석 없이 개발에 뛰어드는 행위
- 주로 쉽게 짜여지나 유지보수하기 비효율적이며 Input cases의 일부만 커버함
- Ad-hoc Testing: 문서화X, 테스트디자인X, 테스트케이스X
- 랜덤으로 일부만 테스트함
주로 시간 없을때 이렇게 됨
- 랜덤으로 일부만 테스트함
- 참고자료: Baeldung CS
Boiler Plate
원래: 19세기에 증기 보일러 만들떄 틀로 사용하던 강철판, 제조정보가 음각으로 새겨져 있음 \(\Rightarrow\) 인쇄 분야로 이어져 반복해서 사용하는 텍스트 (광고/로고) 를 의미
- 컴퓨터 분야에서의 보일러플레이트: 변화없이 여러 군데에서 반복되는 코드
- JAVA에서 한 class 만들때
getter
,setter
모두 정의 필요
- JAVA에서 한 class 만들때
없어져야 하는 것이 아닌, 최소한의 변경으로 재사용할 수 있는 코드
- 참고자료: 프로그래밍 용어사전
Scaffolding
- 원래: 건축현장에서 외벽에 임시로 작업자들이 지나다닐 수 있도록 만든 구조물
- 컴퓨터 프로그래밍에서의 의미:
- 초기 프로젝트의 뼈대 생성
- README, License, dir 구조, 컴파일 설정, 자동으로 생성하는 CLI, UI..
- 일부 MVC (Model-View-Controller) 프레임워크에서 사용하는 의미
- 개발자가 사용하고자 하는 모델을 정의하면 자동으로 관련된 보일러플레이트 코드가 만들어지는 기법
- 초기 프로젝트의 뼈대 생성
- 쉬운 예시:
- \(React\)의 CRA (
create-react-app
) - \(Nest\)의
nest new PROJECTNAME
C#
의 Visual Studio에서의 새 프로젝트 만들기- IntelliJ에서 Java Project 생성…etc
- \(React\)의 CRA (
- 참고자료: 프로그래밍 용어사전
Bootstrap, Bootstrapping
- Boot + Strap (부츠 뒷 부분에 달린 끈 혹은 고리) 합성어
to pull oneself up by one’s bootstraps
- 신발을 신은 채로 자신의 신발의 부트스트랩으로 자신을 들어올린다는 뜻
- 불가능하다는 의미였지만 현재는 자력으로 일을 해낸다는 의미로 변형
- 컴퓨터 분야: 한번 시작하면 외부 도움 없이 스스로 진행하는 행위를 통틀어 칭함
부팅: Bootstrapping의 줄임말
- 전원을 켜는 행위에서 그치는 것이 아닌, BIOS (Basic I/O) 도 포함 (전원 켜질 시 진행)
- 전원 ON
- BIOS에서
CPU
,Memory
등의 주변 HW 진단 - 부팅매체 (Hard Disk)의 \(MBR\) (Master Boot Record)에 저장된 부트로더 (Bootloader, aka Bootstrap Code) 프로그램을
Memory
로 복사 - Bootloader Takes Control : Disk에 있는 OS의 코드를 메모리에 올려서 OS 실행
- OS Takes Control
- \(\Rightarrow\) OS는 셀프 실행 불가 BUT 실행만 되면 외부 도움 없이 컴퓨터 완벽제어
컴파일러
- 스스로 컴파일할 수 있는 컴파일러를 만들어 나가는 과정
- 특정 프언을 만들떄 반드시 컴파일러도 만듦. BUT 초기에는 다른 언어로 그 컴파일러를 만든다
Python
,Java
,C#
,C++
등 대부분 주요 언어에서는 부트스트래핑 과정을 거쳐서 자신의 언어로 컴파일러 생성됨- 주요 장점:
- 컴파일할 언어로 작성하기 때문에 중요한 테스트가 된다
- 컴파일러 개발자들은 컴파일 할 언어만 알면 된다
- 컴파일될 고급 언어로 컴파일러 개발이 가능하다 (보통 저급언어로 고급언어 컴파일러 만드는 경우가 많기 때문)
- 참고자료: 프로그래밍 용어사전
7/4: Rest
뭔지 제대로 알아보지도 않고 6개월간 쓰고 있었음을 인지… 자주 사용하던거라서 그래도 쉽게 읽혔다. 이렇게 사용은 해봤는데 이론이 부족한 부분이 많은 것 같다. 그래도 찝찝했던거 하나씩 짚고 넘어가서 기분이 좋당
REST: Representational State Transfer
HeeJeong Kwon Github
REST의 개념
- 자원의 이름으로 구분하여 정보를 주고 받는 모든것을 의미
- 자원: DB = 학생정보 \(\rightarrow\) 자원 =
students
- 정보: 데이터 요청 시점에서 자원의 상태 (정보) 전달 (주로 \(JSON\) 또는 \(XML\))
- 자원: DB = 학생정보 \(\rightarrow\) 자원 =
- SW 개발 Architecture의 한 종류 (
www
같은 분한 하이퍼미디어 시스템을 위한) - 기본적으로 웹의 기존 기술과
HTTP
프로토콜을 그대로 활용하기 때문에 웹의 장점을 최대한 활용할 수 있는 아키텍쳐 스타일 - 네트워크 상에서 Client와 Server 사이의 통신 방식 중 하나
- 구체적인 개념:
HTTP URI (uniform resource identifier)을 통해 자원 (resource) 을 명시하고 HTTP Method (
POST
,GET
,PUT
,DELETE
) 를 통해 자원에 대한 CRUD Operation을 적용
REST의 장단점
장점 | 단점 |
---|---|
- HTTP 프로토콜의 인프라 그대로 사용, 별도 인프라 구축X - HTTP 프르토콜의 표준을 최대한 활용 -> 장점도 흡수 - HTTP표준의 모든 플랫폼에서 사용 가능 - Hypermedia API의 기본 충실히 지키며 범용성 보장 (??) - 명확하므로 쉽게 의도 파악 가능 - 서버와 클라 역할을 명백하게 분리 | - 표준이 존재하지 않는다 - 사용할 수 있는 메소드가 4가지 밖에 없다 - 브라우저를 통해 테스트할 일이 많은 서비스라면 Header값이 URL보다 더 어렵게 느껴진다 - 구형 브라우저가 아직 제대로 지원불가한 부분 존재 ( PUT , DELETE , pushState 지원X) |
- REST가 필요한 이유
- 애플리케이션 분리 및 통합
- 다양한 클라이언트의 등장
- 다양한 브라우저와 모바일 디바이스에서까지 통신 가능
REST 구성 요소
- 자원 (Resourec): URI
- 모든 자원에 고유한 ID 존재, SERVER에 존재
- 자원 구별하는 ID는 HTTP URI (ex:
/members/:memberId
) - Client: URI이용해서 자원 지정 \(\rightarrow\) 자원의 상태에 대한 조작을 Server에 요청
- 행위 (Verb): HTTP Method
- 표현 (Representation of Resource)
- 서버가 클라의 요청에 따라 적절한 응답 (Representation) 발송
REST 특징
- Server-Client 구조 (자원이 있는 쪽이 Server, 요청하는 쪽이 Client)
- REST Server: API제공, 비즈니스 로직 처리 및 저장
- Client: 사용자 인증이나 context(세션, 로그인 정보)등을 직접 관리하고 책임짐
- \(\Rightarrow\) 서로간 의존성 저하
- Stateless (무상태)
-
무상태성이란? does not save client data generated in one session for use in the next session with that client (독립적인 트랜잭션으로 취급) - HTTP는 Stateless Protocol이므로 REST역시 무상태성
- Client의 context를 Server에 저장 X \(\rightarrow\) 구현 단순화
- Server은 각각의 요청을 완전히 별개의 것으로 인식 후 처리
- 일관성 부여, 자유도 높아짐
-
- Cacheable
- HTTP의 특성 중 하나 (Last-Modfied / E-Tag 으로 캐싱 구현 가능)
- 대량의 요청을 효율적으로 처리하기 위해 필요
- 응답시간 빨라짐, REST Server 트랜잭션 발생X \(\Rightarrow\) 전체 응답시간, 성능, 서버의 자원 이용률 향상
- Layered System (계층화)
- Client는 REST API Server만 호출
- REST Server은 다중 계층 구성 가능
- API Server은 순수 비즈니스 로직 수행
- 그 앞단에 보안, 로드밸런싱, 암호화, 사용자 인증 추가 가능 (구조상 유연성) - PROXY, Gateway같은 네트워크 기반의 중간 매체 사용 가능
- Code-On-Demand (optional)
- Server로부터 스크립트를 받아서 Client에서 실행 (반드시 충족X)
- Uniform Interface (인터페이스 일관성)
- URI로 지정한 Resource에 대한 조작을 통일/한정적인 Interface로 수행
- HTTP 표준 프로토콜에서 따르는 모든 플랫폼에서 사용 가능 (특정 언어나 기술에 종속X)
희정님 깃헙이 정리가 매우 잘 돼 있다. 이분 깃헙 페이지 공부하는 것도 많은 도움이 될듯 싶당 API 분리를 하면서 리팩토링하는 테스크에서 선배들이 한 프로젝트의 부분 보면 PATCH
도 같이 사용하던데 이것도 정확히 뭔지 알아봐야겠다. 아 그리고 컴네 복습 해야겠다…
- 참고자료: HaeeJeong Github
7/6: Error Boundary
장점 & 필요한 이유
이것도 프로젝트에 도입한다고 해서 급하게 공부해봤다. 선언형 프로그래밍이 익숙하지 않아 내일쯤 TIL로 해봐야겠다..Error 처리 중 중복되는 코드들이 보기 싫었는데 뭔가 해결책일 것 같아 흥미롭게 공부해본다
- React 16에서 도입된 컨셉, 쉽고 효과적으로 에러처리 가능
- 카카오페이지에서는 Fetcher 컴포넌트로 API 호출, Error Boundary로 비동기에서 에러 관리
-
Fetcher 컴포넌트는 React 18의 Suspense 컴포넌트에서 derived, 호출/상태관리 가능, 선언형으로 사용 가능...TIL에서 두번 나온 내용인데 꼭 공부해봐야겠다
- 전에 사용하던 방식 ( Error 상태 관리 \(\leftarrow\) 에러 발생하면 에러메시지 보여주는 컴포넌트 렌더링 ) 의 고민 과 Error Boundary가 해결 가능한 것들:
- 선언적으로 에러 처리 방법 (like Fetcher)
- 서버점검, 네트워크 에러 같은 공통 에러 한곳에 처리
- (For better UX) 에러 발생 시 컴포넌트에서 유저가 API 호출을 재시도하여 에러를 리셋할 수 있는 트리거 배치
- 렌더링 중 TypeError 과 같은 예상치 못한 런타임 에러
JS 로 작성한 것 같다
Error Boundary로 에러 처리
Error Boundary: 하위 컴포넌트 트리에서 발생한 에러를 잡아서 선언적으로 처리할 수 있는 컴포넌트
- \(\Rightarrow\) 상위 컴포넌트를
<ErrorBoundary>
로 Wrap하기- 물론 최상위 컴포넌트만 묶는게 아니다. 필요한 컴포넌트들의 상위에 적절히 배치하여 사용
- 공식문서를 보다가 알게 된 사실인데,
function component로는 Error Boundary 작성이 불가능 하다 (static required
)
//App.js
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return { hasError: true };
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return this.props.fallback;
}
return this.props.children;
}
}
...
<ErrorBoundary>
<ExampleApplication/>
</ErrorBoundary>
- Rendering 중 throw된 error을
catch
하도록 동작함 static getDerivedStateFromError
: lets you display error message instead of clearing the UI (주로componentDidCatch
와 동시 사용)
react-error-boundary
- function 정의 가능!
npm i react-error-boundary
or yarn add react-error-boundary
import { ErrorBoundary } from "react-error-boundary";
<ErrorBoundary fallback={<div>Something went wrong</div>}>
<ExampleApplication />
</ErrorBoundary>
- fallback prop에 정의된 함수 추가
import { ErrorBoundary } from "react-error-boundary";
function fallbackRender({ error, resetErrorBoundary }) {
// Call resetErrorBoundary() to reset the error boundary and retry the render.
return (
<div role="alert">
<p>Something went wrong:</p>
<pre>{error.message}</pre>
</div>
);
}
<ErrorBoundary
fallbackRender={fallbackRender}
onReset={(details) => {
// Reset the state of your app so the error doesn't happen again
}}
>
<ExampleApplication />
</ErrorBoundary>;
useErrorBoundary
Hook : shows nearest error boundary from event handler (Dismiss도 가능)import { useErrorBoundary } from "react-error-boundary"; function Example() { const { showBoundary } = useErrorBoundary(); useEffect(() => { fetchGreeting(name).then( response => { // Set data in state and re-render }, error => { // Show error boundary showBoundary(error); } ); }); }
ErrorBoundary cannot be used as a JSX component
- 참고자료: 카카오 FE 기술블로그, React dev, react-error-boundary
7/7: Rest Api
Rest 하다가 너무 길어져서..이어서 쓴다. 올해 해커톤에서 처음으로 API라는걸 보았는데, 팀장님이 각자 API명세서를 써오라고 하셔서 진짜 막막했던 기억이 있다. 근데 해보니 별거 아니었다ㅋㅋ물론 나중에 싹 갈아엎어야 했지만…쫄지말자 일단 하면 된다. 아 그리고 뭔가 프로젝트 공부에 연장선인 것 같은데 백엔드 팀이 API uri를 Restful하게 (더?) 바꾼다고…해서 뭔지 알아볼 필요는 있다!
- API (Application Programming Interface) : 데이터와 기능의 집합을 제공, 컴퓨터 프로그램 간 상호작용 촉진, 정보 교환 가능하도록 함
REST API (Representational State Transfer API) REST 기반 API
최근 OpenAPI
( Google Maps, 공공데이터 etc) , 마이크로 서비스( architecture that a big application is divided into small applications that can be changed, combined..etc ) 대부분 REST API제공- 시스템을 분산해 확장성과 재사용성을 높여 유지보수 및 운용이 편리
- HTTP를 지원하는 언어로 클라/서버 구현 가능
설계 기본 규칙
- Resource 원형
- DOCUMENT: obj instance 나 DB record와 유사한 개념
- COLLECTION: 서버에서 관리하는 Directory라는 RESOURCE
- STORE: 클라이언트에서 관리하는 RESOURCE 저장소
- URI는 정보의 자원을 표현
- RESOURCE: 명사, 소문자
- DOCUMENT 이름으로는 단수 명사 사용
- COLLECTION, STORE 이름으로는 복수 명사 사용
- ex:
GET /Member/1
\(\rightarrow\)GET /members/1
- 자원에 대한 행위는 HTTP Method 로 표현
- Uri에 method가 들어가면 안됨 (
GET /members/delete/1
\(\rightarrow\)DELETE /members/1
) - 행위에 대한 동사 표현 금지 (CRUD 나타내기X)
GET /members/show/1
\(\rightarrow\)GET /members/1
GET /members/insert/2
\(\rightarrow\)POST /members/2
- 경로에 변경가능성 있는 부분은 unique 값으로 대체 (:id
)
- Uri에 method가 들어가면 안됨 (
REST API 설계 규칙
/
: used to show 계층 관계- URI 마지막에
/
포함 X -
: used to enhance readability_
: NOT USED- use lowercase letters (RFC 2986, URI문법 형식은 URI스키마와 호스트 제외 대소문자 구별하도록 규정)
- NO extensions in Uri, but uses Accept header
- WRONG:
http://restapi.example.com/members/soccer/345/photo.jpg
- CORRECT:
GET / members/soccer/345/photo HTTP/1.1 Host: restapi.example.com Accept: image/jpg
- WRONG:
- If resources have relationships:
리소스명/리소스ID/다른리소스
- ex:
/users/{userid}/devices
RESTful의 개념
- RESTful: follows REST 개념
- 목적: 이해하기 쉽고 사용하기 쉬운 REST API 만들기 (일관적인 컨벤션으로 호환성, 이해도 향상)
- RESTful하지 못한 경우:
- CRUD기능을 모두 POST로만 처리하는 API
- route에 resource, id외의 정보가 들어가는 경우 (/students/updateName)
대문자가 들어가면 안된다는건 처음 알았다. 여태 clubId
, memberId
이렇게 했는데 싹 다 바꾸고 있겠구나 싶다..
- 참고자료: (역시나) HaeeJeong Github
7/8: Test with Jest
오늘 2시에 토스 과제 테스트가 있는데 (FE 직무) 실행환경 중 yarn test
라는게 있어서 부랴부랴 Test 공부…항상 언젠가는 다뤄보고 적용해야지 했던 내용인데 이렇게라도 시작해봐서 다행?인것같다. 시간이 별로 없어서 블로그가 아니라 유튜브 보고 해본다 (2시간도 안남음)
Introduction
- JEST : facebook 에서 zero config 철학으로 내놓은 testing library
App.tsx
에 대해 test한다면App.test.tsx
파일에 테스트코드 작성
test('테스트 결과 설명', () => {
expect(TESITNG_FEATURE).toBe(EXPECTED_VALUE) // toBe: Matcher
})
Matchers
toBe(값)
,toEqual(값)
,toStrictEqual(값)
,toBeCloseTo*값)
- 객체/리스트 비교 시
toEqual
사용(재귀적으로 돌기 떄문 ),toStrictEqual
이 나음 - 소수는
toBeCloseTo
사용
- 객체/리스트 비교 시
toBeNull()
,toBeUndefined()
,toBeDefined()
- boolean
toBeTruthy
,toBeFalsy
toBeGreaterThan
,toBeGreaterThanOrEqual
,toBeLessThan
,toBeLessThanOrEqual
toMatch(정규표현식)
""없이 toContain(값)
in array toThrow()
,toThrow(값)
error 여부
비동기 코드 테스트
CALLBACK
- This test will PASS even tho it’s incorrect
// original const getName: (callback) => { // 3초 후 발생 const name = "Mike"; setTimeout(() => { callback(name) }, 3000) } // test test("3초 후에 받아온 이름은 Mike", () => { function callback(name){ expect(name).toBe("Tom"); } getName(callback) // 기다리지 않고 바로 끝내버림 (1 ms) })
- use
done test("3초 후에 받아온 이름은 Mike", done => { function callback(name){ expect(name).toBe("Tom"); done(); } getName(callback) // waits (3023 ms) })
callback API using try-catch
// original
const getName: (callback) => { // 3초 후 발생
const name = "Mike";
setTimeout(() => {
throw new Error('SERVER ERROR')
}, 3000)
}
// test
test("3초 후에 받아온 이름은 Mike", done => {
function callback(name){
try {
expect(name).toBe("Tom");
done()
} catch(error){
done()
}
}
getName(callback) // 기다리지 않고 바로 끝내버림 (3023 ms)
})
PROMISE
- promise는 기다려 주기 때문에 done()이 필요 없음
- 단, test code에서
return
필수 (아니면 다 통과될 것)
Promsie Code
// original
const getAge: () => { // 3초 후 발생
const age = 30;
return new Promise((res, rej) => {
setTimeout(()=>{
res(age); // resolves
})
}, 3000)
}
// test
test("3초 후에 받아온 나이는 30", () => { // done 필요 X
return return getAge().then(age => {
expect(age).toBe(30)
})
})
// or (simplified) using resolves, rejects
test("3초 후에 받아온 나이는 30", () => {
return expect(getAge()).resolves.toBe(30) // rejects로 변경 가능
})
ASYNC Async Code
// original
const getAge: () => { // 3초 후 발생
const age = 30;
return new Promise((res, rej) => {
setTimeout(()=>{
res(age); // resolves
})
}, 3000)
}
// test
test("3초 후에 받아온 나이는 30", async () => {
const age = await getAge();
expect(age).toBe(30)
})
// or
test("3초 후에 받아온 나이는 30", async () => {
await expect(getAge()).resolves.toBe(30)
})
테스트 전후 작업
beforeEach, afterEach
- 파일 내 각 테스트를 돌리기 전/후에 매번 실행될 코드 (초기화 등)
beforeEach(()=> { num = 0 } ) afterEach(()=> { num = 0 } )
beforeAll. afterAll : 전체 테스트 전, 후
describe
beforeAll(() => console.log("밖 beforeAll")) // 순서 : 1
beforeEach(() => console.log("밖 beforeEach")) // 2, 6
afterEach(() => console.log("밖 afterEach")) // 4, 10
afterAll(() => console.log("밖 afterAll")) // 마지막
test('0 + 1 = 1', () => {
console.log("밖 test");
expect(add(0, 1)).toBe(1) // 3
})
describe("Car related tests", () => {
beforeAll(() => console.log("안 beforeAll")) // 5
beforeEach(() => console.log("안 beforeEach")) // 7
afterEach(() => console.log("안 afterEach")) // 9
afterAll(() => console.log("안 afterAll")) // 마지막 - 1
test('0 + 1 = 1', () => {
console.log("안 test");
expect(add(0, 1)).toBe(1) // 8
})
})
test.only, test.skip
test.only('....', ()=> {...}) // 이거만 실행
test.skip('....', ()=> {...}) // 이건 스킵
- 출처: 코딩앙마 Jest시리즈
7/17: React-Query II
계절학기 중간고사 + 쉰답시고 너무 오래 안했다…React Query를 도입해보려고 노력중인데 이론으로 배우는것과 실제로 이미 있던 코드에 추가를 하려고 하다 보니 너무 난관이 많아 조금 더 공부를 해야겠다 싶었다. 나중에 본문 하나로 정리해봐야지
공부는 역시 유튜브로 Web Dev Simplified
- QueryKey Examples
/posts
\(\rightarrow\)["posts"]
/posts/1
\(\rightarrow\)["posts", post.id]
/posts?authorId=1
\(\rightarrow\)["posts", { authorId: 1 }]
/posts/2/comments
\(\rightarrow\)["posts", post.id, "comments"]
- Query Status
postQuery.fetchStatus === "fetching"
postQuery.status === "isLoading"
postQuery.fetchStatus === "idle"
(done)postQuery.fetchStatus === "success | error"
postQuery.fetchStatus === "paused"
(encountered problem)
- Using React Query Dev Tool
fectching
\(\leftrightarrow\)stale
: windowfocus, moving pages, network reconnection…etc- displays cached files while it is refetching (non blank)
- if you want
fetching
\(\rightarrow\)fresh
instead ofstale
- \(\Rightarrow\) in the
App.tsx
’squeryClient
for<QueryClientProvider/>
const queryClient = new QueryClient( { defaultOptions: { queries: { staleTime: 1000 * 60 * 5 }}} ) // 5 min
- don’t want data to go stale unless it’s been in cache for 5 minutes
- or we in each query (example:
postQuery
):const postQuery = useQuery({ queryKey: ["posts"], queryFn: getPosts, staleTime: 1000 * 60 * 50 })
- \(\Rightarrow\) in the
- set Refetching Interval:
const postQuery = useQuery({ queryKey: ["posts"], queryFn: getPosts, refetchInterval: 1000 // refetch every 1 sec })
- enabled key: useful when you want to only render query when another is loaded
const userQuery = useQuery({ queryKey: ["users", postQuery?.data?.userId], enabled: postQuery?.data?.userId != null, queryFn: getUser(postQuery.data.userId), })
Mutation
const createPostMutation = useMutation({
mutationFn: createPost,
onSuccess: ( data, variables, context ) => {
console.log(context) // {hi: Bye}
},
onError: ( error, variables, context ) => ...,
onSettled: ( data, error, variables, context ) => ..., // FINALLY
onMutate: (variables) => { // Called BEFORE MUTATION FUNCTION
// SET CONTEXT
return {hi: 'Bye'}
},
retry: 3 // not really recommeneded
})
createPostMutation.mutate()
createPostMutation.mutate({} { onError }) // 여기다가도 추가 가능!
createPostMutation.mutateAsync() .then(()=>{}) // Uses PROMISES
- invalidateQueries
const createPostMutation = useMutation({ mutationFn, onSuccess: data => { queryClient.invalidateQueries(['posts']) // refreshes every (ex: ['posts', 1] as well) //TOBE: queryClient.invalidateQueries(['posts'], { exact: true }) // MANUALLY ADD IT TO CACHE queryClient.setQueryData(['posts', data.id], data ) } })
- pagination in React Query
- will show previous data WHILE REFETCHING data
const [page, setPage] = useState(1) const { status, error, data, isPreviousData } = useQuery({ queryKey: ['posts', { page }], keepPreviousData: true, queryFn: () => getPostsPaginated(page) })
7/18: React-Query III
배운게 겹쳐도 그냥 계속 해야겠다. React-Query 관련 좋은 벨로그 포스트 시리즈를 발견해서 가볍게 며칠 걸리더라도 공부해보는게 좋다고 생각한다.
- ALL FROM: Hayden Velog 시리즈!!
Introduction
- States (상태)
- State란: 개발자 입장에서 관리해야 하는 데이터들 (시간에 따라 변경이 된다)
- 모던 FE에서는 관리해야하는 상태가 확연히 늘어남
- Props Drilling 등의 이슈로 전역상태관리 라이브러리들도 사용
- Client State vs Server State
- where ownership exists
- \(\Rightarrow\) 사실상 FE에서 Server state가 저장되어 있는 state들은 일종의 캐시이다
Client State Server State Client에서 소유하며 온전히 제어 가능 원격의 공간에서 관리 / 유지됨 초기값 설정이나 조작에 제약사항 X Fetching / Updating 에 비동기 API 필요 다른 사람들과 공유 X, Client내 UIUX 흐름이나 Interaction에 따라 변화 가능 다른 사람들과는 공유됨, 사용자가 모르는 사이에 변경 가능 항상 Client 내에서 최신 상태로 관리됨 신경쓰지 않는다면 잠재적으로 out of date
가 될 가능성 지님 - React Query 사용:
QueryClientProvider
로 묶어주기
Queries: data fetching
const members = useQuery('members', fetchMembers) //또는
const members = useQuery('members', ()=>{fetchMembers(memberId)}, options)
- Query Key: key, value 매핑 구조
- query key에 따라 query caching 관리
- string, 형태, array 형태로 나뉘어져 있음
- Query Function: Promise를 반환하는 함수 (data resolved or throw error)
useQuery
RETURN VALUES:const { data, error, isFetching, status, isLoading, isSuccess, isLoading, refetch, remove } = useQuery(....)
refetch
: 해당 쿼리 refetch하는 함수 제공remove
: query cache에서 지우는 함수 제공isLoading
: no cached data +isFetching
useQuery
OPTIONS:const userQuery = useQuery('users', fetchUsers, { onSuccess: data => console.log(data), onError: e => console.log(e.message), onSettled: () => {}, retry: 0 // retry 횟수 select: (data) => { return data?.someKey}, //성공 시 가져온 data 가공해서 전달 keepPreviousData: true,// 새롭게 fetching 시 이전 데이터 유지 여부 refetchInterval: 1000 // refetch every 1 sec // render query only after another is loaded (or after some event) enabled: postQuery?.data?.userId != null, staleTime: 1000 * 60 * 50, // don't stale unless cached for 5 min retechOnMount: false, refetchOnWindowFocus: false, refetchOnReconnect: false, })
Mutations : Post, Put, Delete
- postMembermutation
.mutate()
: mutation 실행- postMembermutation
.mutate(newMemberData)
- postMembermutation
- postMembermutation
.mutateAsync()
: 위와 유사 하지만 PROMISE 반환 postMembermutation
.reset()
: 초기화useMutation
OPTIONS:const userQuery = useQuery('users', fetchUsers, { onMutate: data => console.log(data), })
onMutate
: mutation 동작 전 동작하는 함수, optimistic update 적용 시 유용 (context 적용하기 좋음 7/17 내용 참고)- 나머지는
useQuery
와 비슷하다 (오히려 더 적음)
Query Invalidation
// 캐시 안 모든 쿼리 재실행
queryClient.invalidateQueries()
// 특정 query만 재실행
queryClient.invalidateQueries('members')
해당 Key를 가진 Query는 Stale 취급, 현재 rendering 되고 있는 query들은 백그라운드에서 refetch 중
cacheTime
: 메모리에 얼마만큼 있을건지 (default = 5분, 해당 시간 이후 Garbage Collection에 의해 처리)useQuery
가 활성화된 후 존재 시간- query goes into “cold storage” if no active
useQuery
- data garbage collected after cache expired (후에 클라는 데이터 사용불가)
staleTime
: 얼마의 시간이 흐른 후에 데이터를 stale 취급할 것인지 (default = 0)[참고] Fresh and Stale -ChatGPT
- FRESH: 데이터가 fetched 되고 캐시에 저장된 상태
- \(\Rightarrow\) up-to-date with the latest data from the server
- STALE: 데이터 refetch가 trigger 될 때의 캐시된 데이터의 상태
- 캐시에 오래 있으면 저절로 fresh에서 stale이 됨 (
staleTime
(aka max age)으로 조절가능) - 비록 최신버전은 아니지만 새로운 데이터를 refetch 할 동안 캐시에서 불러와 재사용이 가능하다 (fetching 도중 아예 아무것도 안보여주는 것보다는 전 데이터라도 보여주는게 UX면에서 좋음)
- 데이터 refetching only triggers for stale data
- 캐시에 오래 있으면 저절로 fresh에서 stale이 됨 (
Stale 데이터가 application에서 사용되면 React Query는 바로 refetch를 해서 최신상태로 업데이트를 하려고 한다 \(\rightarrow\) API get을 줄이기 위해서는 fresh상태로 두는게 중요
- TMI: 왜 staleTime이 default = 0으로 설정되어 있는거
- “How is the data on the screen always up to date?” is a better question than
- “Why is my data not updating?!” -Tanner Linsley Twitter
- FRESH: 데이터가 fetched 되고 캐시에 저장된 상태
장점 및 고려사항
- 장점
- 서버상태 관리 용이 (redux 나 mobx 비해), 직관적인 API 호출 코드
- API 처리에 관한 각종 Interface + Option 제공
- Boilerplate 코드 감소 (Client Store은 진짜 FE에서만 사용되는 전역상태만 남아 store답게 사용됨)
- devtool 제공 (디버깅 원활)
- Cache 전략 필요시 매우 유용
- 고민이 필요한 부분
- Component 상대적으로 비대해짐 (설계/분리에 대한 고민 필요)
- 난이도가 높아진 프로젝트 설계 (Component 유착 최소화 및 사용처 파악 필요)
- React Query의 장점을 더 잘 활용한 방법 (단순 API 통신 이상의 가능성)
- 추천 사유
- 너무 많은 전역상태 (비대해진 store)
- API 코드 간편화
- FE에서 데이터 Caching 전략
7/19: OOP Revisited
클린코드 읽는데 추상화 개념이 가물가물해서 가볍게 리뷰
- Before OOP: Procedural Programming (functions and vars inside program)
- PROBLEM: SPAGHETTI CODE (too much interdependency)
- \(\Rightarrow\) OOP, split into units called objects with properties (var) & methdods (funct)
4 PILLARS: Encapsulation, Abstraction, Inheritance, Polymorphism
Encapsulation
- group related variables and functions (using those vars) into object
- no (or less) parameters in function \(\rightarrow\) clean code
easy to use and maintain
- procedural:
let baseSalary = 30_000; let overtime = 10; let rate = 20; functon getWage(baseSalary, overtime, rate) { return baseSalary + (overTime * rate) }
- OOP:
let employee = { baseSalary: 30_000, overtime: 10, rate: 20, getWage: function() { return this.baseSalary + (this.onvertime * this.rate) } } employee.getWage()
Abstraction
- hiding implementation details and showing only the functionality to the users
like DVD player: complexity hidden, user can just click button and not know the logic behind it
- code example:
function Employee(name, age, baseSalary){ this.name = name; this.age = age; this.baseSalary = baseSalary; this.monthlyBonus = 1000; this.calculateFinalSalary = function (){ let finalSalary = this.baseSalary + this.monthlyBonus; console.log(finalSalary) } this.getEmpDetails = function (){ console.log('NAME: '+this.name+' AGE: ' + this.age ) } }
- and we call:
let emp1 = new Employee('John', 30, 2000); emp1.getEmptDetails(); // NAME: John AGE: 30 emp1.calculateFinalSalary(); // 3000
emp1.calculateFinalSalary()
,emp1.monthlyBonus
shouldn’t be accessed \(\rightarrow\) must hide it- ex:
emp1.monthlyBonus = 10000;
하면 정상작동됨 \(\Rightarrow\) PROBLEM!
- ex:
- and we call:
this.monthlyBonus = 1000;
\(\Rightarrow\)let montlyBonus = 1000;
- \(\rightarrow\) can’t be access from outside, BLOCKSCOPE (same with
calculateFinalSalary
function)function Employee(name, age, baseSalary){ this.name = name; this.age = age; this.baseSalary = baseSalary; let monthlyBonus = 1000; // change to block scope let calculateFinalSalary = function (){ // can't be accessed from outside let finalSalary = baseSalary + monthlyBonus; console.log(finalSalary) } this.getEmpDetails = function (){ console.log('NAME: '+this.name+' AGE: ' + this.age ); calculateFinalSalary() // abstraction } }
- \(\rightarrow\) can’t be access from outside, BLOCKSCOPE (same with
- simpler interface (shows only essential features)
- reduce impact of change (외부에서 변경못하도록)
Inheritance
- eliminate redundant code
- ex:
HTMLElement
: [hidden, innerHTML, click(), focus()]Textbox
,Select
,Checkbox
inherit these
- Parent \(\longrightarrow\) child
- Super \(\longrightarrow\) sub
- Base \(\longrightarrow\) derived
Code Example
class Car{
setName(name){
this.name = name;
}
startEngine(){
console.log('ENGINE STARTED FOR ' + this.name)
}
stopEngine(){
console.log('ENGINE STOPPED FOR ' + this.name)
}
}
class Toyota extends Car { // child class of Car
topSpeed(speed){
console.log('Top speed for ' + this.name + ' is ' + speed );
}
}
- how to call:
let myCar = new Toyota()
myCar.setName('Camry');
myCar.startEngine();
myCar.topSpeed(200);
Polymorphism
- many + forms
- can get rid of if-else, switch blocks
switch (...){ case 'select': renderSelect(); case 'text': renderText(); case ... }
- 이게 아니라 그냥 다 각자
select
,text
안에render()
이 있음 element.render()
:render()
method will behave differently
BENEFITS
Encapsulation | reduce compelexity + increase reusability |
Abstraction | reduce compelexity + isolate impact of changes |
Inheritance | eliminate redundant code |
Polymorphism | refactor ugly switch/case statements |
- 참고자료: Youtube Automation Step by Step
7/20: BFS, DFS
백준 solved ac 통계를 보면 내 DP는 골드인데, 그래프는 실버5다…코테 중 한개는 꼭 그래프 문제 나온다고 하니 이제 슬슬 자료구조 알고리즘을 제대로 복습해야 겠다고 생각이 들었다.
- Study Notes Section에 따로 정리! BFS, DFS
7/22: null vs undefined
클린코드 챕터 8을 읽는 도중 개발 초기에 null
과 undefined
를 마음대로 썼던게 기억나서 한번 리뷰해보려고 한다. PR을 올렸을 때 선배님이 null은 반환 안하는게 어떻냐고 리뷰를 달아주셨는데 이제야 알 것 같다.
- Typescript 2.0 전에는 numbers나 strings 등에 사용 가능 했었음 \(\rightarrow\)
TypeError
,ReferenceError
유발 - version 2.0에 StrictNullChecks 사용가능하게 등장 \(\rightarrow\)
null
이나undefined
사용 시 compile-time error 호출
Undefined
: value is not assigned & you don’t know its value
- unintentional absense of value
- variable declared but not yet been assigned a value
- Typescript가 자동으로 선언함 (assigned 안될 시)
- data type: undefined
let a: undefined console.log( typeof(a) )
- to number:
console.log( Number(a) )
=NaN
null
: you know that field doesn’t have value
- intentional absense of value
- 개발자의 고의로만 선언됨 (Typescript는 절대로 이렇게 선언 X)
-
we have to assign Null to make it null
-
- data type: object (JS BUG이니 null에는 typeof 쓰지 말것)
let b: null = null console.log( typeof(b) ) // object 반환 -> JS BUG
- to number:
console.log( Number(b) )
=0
undefined and null
- falsy, but not false (nor true)
let a=undefined let b=null if (!a) console.log('false') //false if (!b) console.log('false') //false
let a=undefined let b=null if (a==false) console.log('false') if (a==true) console.log('true') if (b==false) console.log('false') if (b==true) console.log('true')
- checking for null & undefined:
- \(\Rightarrow\)
==
,===
둘 다 사용 가능
- \(\Rightarrow\)
- comparing null with undefined
console.log(null == undefined) // true console.log(null === undefined) // false
==
: equality checker works===
: strict equality checker은 안됨 (Data Type까지 확인)
- 연산자 활용:
strictNullChecks === false
면 Number()로 된거 활용true
라면 Compile Error 던짐
- 참고자료: Tektutorialshub
7/25: Framework and Library
React는 Framework가 아니라 Library 이다! 라고 여러번 들었지만 그 내용이 와닿지 않아 정확한 차이점을 보려고 한다.
Framework
Framework: 뼈대/구조
- \(Frame\) (틀, 규칙/법칙) + \(work\) (SW의 목적)
- 목적에 따라 효율적으로 구조를 짜놓은 개발 방식
- Application 개발 시 필수적인 코드, 알고리즘, DB연동 등과 같은 기능들을 위해 어느정도 뼈대/구조를 제공해 주는 것
-
Spring, Vue Js - ‘ready-to-use’ tools, standards, and templates for fast application development
- Library 와의 차이점: Framework has control over the flow (흐름에 대한 제어 권한을 자체적으로 보유)
Library
Library: 특정 기능에 대한 도구/함수들을 모아둔 집합
- React는 Framework가 아닌, 라이브러리이다
- 흐름에 대한 제어 권한을 사용자가 가지고 있음
Module: 구성 단위, 구성 부분
- 별도의 파일로 분리된 독립된 기능
- 결론적으로 자주 사용하게 되는 코드를 하나의 함수나 클래스라는 단위로 묶어 재사용성을 높이는 것
- 개발에서는 Library와 동일한 의미, 코드의 집합체
- 순수 JS 에서는 모듈이라는 개념이 분명하게 존재하지 않는다고 한다
Plugin: 어떤 특정한 하나의 문제를 해결하기 위한 컴포넌트
- 일일이 구현된 것이 아닌, 필요한 기능들만 찾아 사용할 수 있도록 미리 만들어 둔 것
- 라이브러리 보다 더 작은 개념
- Library = Set of Plugins
import
로 사용하기에 Library와 거의 동일시 됨
제어의 역흐름
- IOC: Inversion of Control
전통적인 프로그래밍에서 흐름은 프로그래머가 작성한 프로그램이 외부 라이브러리의 코드를 호출해 이용한다. 하지만 제어 반전이 적용된 구조에서는 외부 라이브러리의 코드가 프로그래머가 작성한 코드를 호출한다
Framework | Library |
정해진 프로그램의 틀에 맞게 사용자가 필요한 기능을 입력 | 호출하는 개발자가 필요한 기능을 원할 떄 호출 |
프레임워크가 내 코드를 호출 | 내 코드가 라이브러리를 호출 |
this-is-blog 의 사진 예시
React as a Library
React: UI를 만들기 위한 JS Library
- Reactjs.org - React는 전체적인 eco-system을 제공하지 않음
- built-in feature의 부재, third-party libraries 적극사용됨
react-router
,redux-forms
,formik
,axios
or fetch API…etc- “unopinionated”: 정답이 없으며 여러 option이 있음
- VERY FLEXIBLE
- framework는 전체적인 eco-system을 제공함
- nextjs: React js 의 Framework
- routing, server-side rendering, image 최적화 등을 제공함
- nextjs: React js 의 Framework
- 참고자료: doozi, Joei Kim Github Pages, this-is-blob, TJ WebDev