리스트가 길어지면서 1개 row 업데이트시 전체 2500개리스트와 하위컴포넌트를 전부 리랜더링 하는 방식때문에 지연이되는 문제가 되고있다.
export default React.memo(TodoListItem);
const App = () => {
const [todos,setTodos] = useState(createBulkTodos);
const onToggle = useCallback((id) =>{
// 전체리스트(배열)을 map()으로 순회
setTodos(todos.map(todo =>
// {spread 연산자}로 todo for문 돌리고, todo.checked 값을 반전함
todo.id === id? {...todo, checked:!todo.checked} : todo)
);
},[]
);
const nextId = useRef(2501); //id 관리용으로 다음번호를 붙여줌
const onInsert = useCallback((text) => {
const nextTodo={id:nextId.current, text:text, checked:false};
setTodos(todos.concat(nextTodo));
nextId.current = nextId.current + 1;
},[]
);
//TodoListItem에서 remove button click -> id
const onRemove = useCallback(
(id) => {
setTodos(todos.filter(todo => todo.id !== id));
},[]
);
1~2번 랜더링 속도 개선 결과
react-virtualized 적용
기존의 값을 직접 수정하지 않으면서 새로운 값을 만들어 내는 것
immer 라이브러리를 사용하면 쉽게 작업가능
const a = [{id:1}];
const b = [...a];
console.log(a === b); // false, 새로만듬
console.log(a[0] === b[0]); // 얕은 복사(참조값을 복사)로 인해 같은 값으로 인식
b[0] = {id:1};
console.log(a[0] === b[0]); // 깊은 복사(새로 객체를 만들어 신규참조값을 생성)로 다른 값으로 인식
클라이언트가 우리쪽시스템에 접근했을때 어떤 플로우를 거쳐 리스폰스가 나가는지
test.com의 서브도메인으로 점점 늘려가야 한다. Rout53에서 설정가능
email.test.com
api.test.com
bo.test.com 백오피스
ip를 할당하고 보안그룹설정
aws 가용영역: 서버가 위치할수 있는 물리적 영역
리전이 동일해도 다른 가용 영역에 AWS 서비스를 각각 배치했다면 물리적으로는 복수의 데이터센터를 사용.
만약 총 abc 영역이 있는데 2개를 쓴다면 a에 하나, c에 하나 세팅하는 편이 좋다
출처: https://kangmin517.tistory.com/92 [ARTIFEX;):티스토리]
이 서버는 무엇이고, 어떻게 사용(역할)되고 있다를 서술해 주는것이 좋다.
웹 서버와 WAS 서버의 차이 참조
aws codecommit
배포시 대고객 서비스에서 중요한 포인트:
java, spring boot 버전정도는 기입, spring boot 는 내장톰캣 사용을 가정하므로 톰캣을 따로 표시할 필요는 없다.
로드밸런서 프론트와 따로 구성
RDS에 연결
CSS, js 파일을 올리고, 클라우드 프론트로 엮어서 CDN(캐싱)처리한다.
클라우드 프론트는 빨리 해야할때!, 글로벌 서비스를 진행할때 해당 리전 클라우드 프론트에 정적리소스를 제공
계정관리시? IAM https://docs.aws.amazon.com/ko_kr/IAM/latest/UserGuide/introduction.html
https://junhyeong-jang.tistory.com/22
ssh로 나머지 ec2에 접근
웹 서버는 정적인 파일(예: HTML, CSS, 이미지)을 제공하는 역할을 합니다. 대표적으로 아파치(Apache)와 엔진엑스(Nginx)가 있습니다. 이들 웹 서버는 클라이언트의 요청을 받아들이고, 그에 맞는 정적 파일을 응답으로 제공합니다. 웹 서버는 주로 웹 페이지의 전달과 같은 단순한 기능을 수행하는 역할을 담당합니다.
한편, WAS 서버는 동적인 콘텐츠를 생성하고, 데이터를 처리하는 역할을 합니다. WAS는 웹 애플리케이션을 실행하기 위한 서버로, 사용자의 요청에 따라 데이터베이스 조회, 비즈니스 로직 처리 등 다양한 작업을 수행합니다. 웹 애플리케이션의 실행 환경을 제공하며, 다른 서버와의 통신을 통해 필요한 데이터를 가져와 동적인 응답을 생성합니다.
이처럼, 웹 서버와 WAS 서버는 각각 다른 역할과 기능을 가지고 있습니다. 웹 서버는 정적인 파일 제공에 특화되어 있고, WAS 서버는 동적인 콘텐츠 생성과 데이터 처리에 특화되어 있습니다. 때문에, 웹 애플리케이션을 구성할 때는 웹 서버와 WAS 서버를 함께 사용하여 역할을 분담하고 최적의 성능을 내도록 하는 것이 일반적입니다.
톰캣은 WAS서버입니다..!
apache,Nginx(엔진엑스)는 웹 서버
다른 주소에서 다른 화면을 보여주는 것을 라우팅이라고 한다.
클라이언트 사이드에서 이루어지는 라우팅을 구현하여 SPA를 쉽게 만들어 주는 라이브러가 react-router다.
싱글페이지는 따라서 처음만 html로 받아온다.
멀티페이지인 경우 페이지별 url이 존재.
Link 컴포넌트는 클릭하면 다른 주소로 이동시켜주는 컴포넌트다. a 태그를 사용해도 페이지 전환이 되지만 그 경우 페이지를 새로 불러오기 때문에 애플리케이션이 갖고 있는 모든 상태가 초기화된다.
Link 컴포넌트를 사용하여 페이지를 전환하면, 페이지를 새로 불러오지 않고 애플리케이션은 그대로 유지한 상태에서 HTML5 History API를 사용하여 페이지의 주소만 변경해준다. Link 컴포넌트 자체는 a 태그로 이루어져 있지만, 페이지 전환을 방지하는 기능이 내장되어 있다.
Link를 사용하여 이동하면 localhost 주소가 바뀌지않아 SPA에 부합, 배포시에도 오류가 발생하지 않는다.
a 링크는 페이지를 새로 로드하므로 spa에 부합하지 않으며, mpa에 적합하다.
import React from 'react';
import { Link } from 'react-router-dom';
const Home = () => {
return (
<div>
<h1>홈</h1>
<p>가장 먼저 보여지는 페이지</p>
<Link to ="/about">소개</Link>
</div>
);
};
export default Home;
Link 사용시 SPA로 네트워크상 동작확인, 그러나 사용자가 url을 직접 입력할수도 있음
1. Profile 컴포넌트
home 에서 링크로 접근시에 대응됨
import React from 'react';
import { Link } from 'react-router-dom';
const Home = () => {
return (
<div>
<h1>홈</h1>
<p>가장 먼저 보여지는 페이지</p>
<Link to ="/about">소개</Link>
<ul>
<li><Link to ="/profiles/user1">user1 프로필</Link></li>
<li><Link to ="/profiles/user2">user2 프로필</Link></li>
</ul>
</div>
);
};
export default Home;
url 직접 접근시에도 대응됨
import React from 'react';
import { useParams } from 'react-router-dom';
const Profile = () => {
const params = useParams();
const profile = params.id;
return (
<div>
<h1>사용자 프로필</h1>
<p>profile id : {profile}</p>
</div>
);
};
export default Profile;
import React from 'react';
import { Route, Routes } from 'react-router-dom';
import Home from './pages/Home';
import About from './pages/About';
import Profile from './pages/Profile';
const App = () => {
return (
<Routes>
<Route path='/' element={<Home/>}/>
<Route path='/about' element={<About/>}/>
<Route path='/profiles/:id' element={<Profile/>}/>
</Routes>
);
};
export default App;
import React from 'react';
import { useLocation } from 'react-router-dom';
const About = () => {
const location = useLocation();
return (
<div>
<h1>소개</h1>
<p>리액트 라우터를 사용해 보는 프로젝트</p>
<p>쿼리스트링 : {location.search} </p>
</div>
);
};
export default About;
import React from 'react';
import { useLocation, useSearchParams } from 'react-router-dom';
const About = () => {
const[searchParams, setSearchParams] = useSearchParams();
const detail = searchParams.get('detail');
const mode = searchParams.get('mode');
return (
<div>
<h1>소개</h1>
<p>리액트 라우터를 사용해 보는 프로젝트</p>
<p>detail : {detail} </p>
<p>mode : {mode} </p>
</div>
);
};
export default About;
Outlet에 중첩 라우팅 내용이 표시됨
import React from 'react';
import { Route, Routes } from 'react-router-dom';
import Home from './pages/Home';
import About from './pages/About';
import Profile from './pages/Profile';
import Articles from './pages/Articles';
import Article from './pages/Article';
import Layout from './pages/Layout';
const App = () => {
return (
<Routes>
<Route element={<Layout />}>
<Route path='/' element={<Home/>}/>
<Route path='/about' element={<About/>}/>
<Route path='/profiles/:id' element={<Profile/>}/>
</Route>
<Route path='/articles' element={<Articles/>}>
<Route path=':id' element={<Article/>} />
</Route>
</Routes>
);
};
export default App;
import React from 'react';
import { Outlet } from 'react-router-dom';
const Layout = () => {
return (
<div>
<header style={{background:'lightgray', padding:16, fontSize:24}}>
header
</header>
<main>
<Outlet />
</main>
</div>
);
};
export default Layout;
import React from 'react';
import { Outlet, useNavigate } from 'react-router-dom';
const Layout = () => {
const navigate = useNavigate();
const goBack = () =>{
navigate(-1);
};
return (
<div>
<header style={{background:'lightgray', padding:16, fontSize:24}}>
header
<button onClick={goBack}>뒤로가기</button>
</header>
<main>
<Outlet />
</main>
</div>
);
};
export default Layout;
2. 히스토리가 남으면 안되는 페이지의 경우 replace처리
navigate('/articles',{replace:true});
바로 이전페이지 이력을 지움
import React from 'react';
import { Outlet, useNavigate } from 'react-router-dom';
const Layout = () => {
const navigate = useNavigate();
const goBack = () =>{
navigate(-1);
};
const goArticles = () => {
navigate('/articles',{replace:true});
}
return (
<div>
<header style={{background:'lightgray', padding:16, fontSize:24}}>
header
<button onClick={goBack}>뒤로가기</button>
<button onClick={goArticles}>목록보기</button>
</header>
<main>
<Outlet />
</main>
</div>
);
};
export default Layout;
import React from 'react';
import { NavLink, Outlet } from 'react-router-dom';
const Articles = () => {
const activeStyle ={color:'green',fontSize:21};
return (
<div>
<Outlet />
<NavLink to='/articles/1' style={({isActive}) => (isActive ? activeStyle : undefined)}>게시글1</NavLink>|
<NavLink to='/articles/2' style={({isActive}) => (isActive ? activeStyle : undefined)}>게시글2</NavLink>|
<NavLink to='/articles/3' style={({isActive}) => (isActive ? activeStyle : undefined)}>게시글3</NavLink>|
</div>
);
};
export default Articles;
NotFound는 Route 가장 맨 아래에 입력합니다.
맨 아래에 위치시키는 이유는 react-router-dom은 페이지를 이동시킬 때
Route를 url에 입력된 내용과 일치하는지 위에서부터 아래로 확인하면서 내려오기 때문에
NotFound 페이지를 가장 맨 아래에 위치시킵니다.
여기서 *
는 '와일드카드문자'로써 '전체'를 의미합니다.
즉, path가 정해져 있지 않은 경로를 입력하면 모두 NotFound 페이지를 보게 되는 것입니다.
출처: https://anerim.tistory.com/226 [디발자 뚝딱:티스토리]
import React from 'react';
import { Route, Routes } from 'react-router-dom';
import Home from './pages/Home';
import About from './pages/About';
import Profile from './pages/Profile';
import Articles from './pages/Articles';
import Article from './pages/Article';
import Layout from './pages/Layout';
import NotFound from './pages/NotFound';
const App = () => {
return (
<Routes>
<Route element={<Layout />}>
<Route path='/' element={<Home/>}/>
<Route path='/about' element={<About/>}/>
<Route path='/profiles/:id' element={<Profile/>}/>
</Route>
<Route path='/articles' element={<Articles/>}>
<Route path=':id' element={<Article/>} />
</Route>
<Route path='*' element={<NotFound />}/>
</Routes>
);
};
export default App;
워크벤치
dbeaver
https://mdwgti16.github.io/spring%20boot/spring_boot_mybatis_multi/#