- [노마드코더] NextJS 시작하기 #1
- [노마드코더] NextJS 시작하기 #2
- [노마드코더] NextJS 시작하기 #3
- [노마드코더] NextJS 시작하기 #4
- [노마드코더] NextJS 시작하기 #5
전에 했던 강의에 이어서 이제 로딩페이지를 작업해보려고 한다.
영화 데이터가 담긴 movies변수는 영화데이터를 불러오기 전까지는 빈 배열 상태로 있을 것이다. 왜냐면 우리가 useState를 사용할때 초기값을 []으로 했기 때문에. 그러나 만약 useState();로 하면 오류가 발생한다. 왜냐면 map을 사용하려고 할때 movies가 undefined이기 때문이다.
그렇기에 우리는 useState() 로 선언하고, 대신에 movies에 값이 있는지를 확인 후 없다면 로딩을 보여주도록 해야한다.
우선 map을 작동하기 전에 movies에 값이 있는지 확인하기 위해서 ?을 사용해주자.
그리고 그전에 movies의 상태를 체크해서 값이 없다면 Loading 문구가 나오도록 해주자.
return (
<div>
<Seo title="Home" />
{!movies && <h4>Loading...</h4>}
{movies?.map((movie) => (
<div key={movie.id}>
<h4>{movie.original_title}</h4>
</div>
))}
</div>
);
이제 API Key를 숨겨볼 건데 그전에 니꼬쌤이 만들어놓은 css를 적용하고 싶다면 return부분만 아래 코드로 대체하면 된다.
return (
<div className="container">
<Seo title="Home" />
{!movies && <h4>Loading...</h4>}
{movies?.map((movie) => (
<div className="movie" key={movie.id}>
<img src={`https://image.tmdb.org/t/p/w500/${movie.poster_path}`} />
<h4>{movie.original_title}</h4>
</div>
))}
<style jsx>{`
.container {
display: grid;
grid-template-columns: 1fr 1fr;
padding: 20px;
gap: 20px;
}
.movie img {
max-width: 100%;
border-radius: 12px;
transition: transform 0.2s ease-in-out;
box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;
}
.movie:hover img {
transform: scale(1.05) translateY(-10px);
}
.movie h4 {
font-size: 18px;
text-align: center;
}
`}</style>
</div>
);
API Key는 유료일수도 있고, 사용량에 제한이 있을 수도 있다. 이런 key가 노출된다면 다른 사람들이 남용할 수 있기 때문에 숨겨주는 것이 좋다. 이름만 봐도 숨겨야할 것 같다. 이 key는 개발자도구의 network창에서 잘 찾아보면 찾을 수 있다. 현재 우리 같은 경우에도 숨기지 않았기 때문에 찾을 수 있다.
이제 이것을 숨겨보려고 한다.
request에 마스크를 씌우는 것과 같은 redirect와 rewrite가 있다.
Redirect
next.config.js파일을 열어서 redirect를 해주는 방법이 있다.
우선 다음과 같이 입력하면 /contact 로 접속하면 /form 으로 이동하게 해준다. permanent는 영구적인 것인지에 대한 여부를 적어준다.
const nextConfig = {
reactStrictMode: true,
async redirects() {
return [
{
source: "/contact",
destination: "/form",
permanent:false,
},
];
},
};
config파일을 수정했으니 서버를 restart해준다.
그리고 주소창뒤에 http://localhost:3000/contact를 입력하고 엔터치면 /form으로 바껴있는 것을 볼 수 있다.
destination 주소는 외부사이트 주소로 작성해도 가능하다.
또한 이런식으로 뒤에 경로는 그대로 가져간채 가운데 부분만 변경하고 싶을때는 다음과 같이 작성한다.
source: "/old-blog/:path",
destination: "/new-blog/:path",
아니면 이것도 가능하다
source: "/old-blog/:path*",
destination: "/new-blog/:path*",
뒤에 오는 모든 경로를 그대로 가지고 이동한다.
마지막 코드를 둔 상태로 rewrite를 사용해보도록 하자.
Rewrite
rewrites는 redirecs와 비슷하지만 다른점은 사용자가 알아차릴 수 없다는 점이다. redirects를 사용하면 직접 눈으로 url이 바뀌는걸 볼 수 있다. 하지만 rewrites를 사용한다면 url은 바뀌지 않는다.
그렇다면 바로 API Key를 숨길 수 있도록 아래와 같이 redirects밑에 작성해준다. key와 주소는 index.js에서 가져온다.
const API_KEY = "[Key]";
module.exports = {
reactStrictMode: true,
async redirects() {
return [
{
source: "/old-blog/:path*",
destination: "/new-blog/:path*",
permanent: false,
},
];
},
async rewrites() {
return [
{
source: "/api/movies",
destination: `https://api.themoviedb.org/3/movie/popular?api_key=${API_KEY}`,
},
];
},
};
그리고 이제 서버를 재시작해준다.
index.js에 가서 기존에 있던 key를 지우고 api 주소는 source 주소로 변경해준다.
useEffect(() => {
(async () => {
const { results } = await (await fetch("/api/movies")).json();
setMovies(results);
})();
}, []);
그리고 주소창에 http://localhost:3000/api/movies 을 입력하면 데이터 정보를 볼 수 있고, 아까와 같이 네트워크 탭에 들어가서 확인하면 주소가 /api/movies로 되어있는 것을 확인할 수 있다.
정리하면, 만약 외부에 노출시키고 싶지 않은 주소가 있는 경우 rewrites를 사용해서 숨길 수 있다.
또 API Key를 config파일에 작성하고 싶지 않다면 .env파일을 생성해서 관리해줄 수도 있다.
next.config.js
const API_KEY = process.env.API_KEY;
.env
API_KEY = [Key]
그리고 항상 .gitignore파일에 .env를 추가해줘야한다.
Server Side Rendering
현재까지 만든 메인페이지에서 코드보기를 하면 Loading이 화면에는 보이진 않지만 페이지소스보기에서는 볼 수 있다. 페이지소스보기를 보면 현재 영화데이터가 존재함에도 불구하고 페이지소스에는 영화데이터는 존재하지 않고, Loading, 즉 데이터가 들어오지 않은 초기상태를 기반으로 이루어져있다.
그러나 어떤 경우는 이런 Loading을 보여주고 싶지 않을 수도 있다. 데이터 fetch나 server에서 발생하는 모든 데이터 작업을 마친후 render되기를 원하는 경우다.
우리는 Next에서 getServerSideProps를 사용해서 이것을 구현할 수 있다.
export async function getServerSideProps() {}
이 함수 안에 작성하는 모든 코드는 server에서 작동한다. 그렇기 때문에 API Key를 여기 안에서 사용하면 client에게 보여질 일이 없기 때문에 rewrite를 사용하지 않고 Key를 숨길 수 있다.
그렇다면 아까 api를 사용하는 부분을 이 함수안에 작성해주고, return안에 props에 results를 추가해준다. 그리고 Home 안에서 return 위에 적었던 모든 코드를 지워준다. 패치안에 url은 서버에서 실행되기 때문에 풀 url을 적어야한다.
export async function getServerSideProps() {
const { results } = await (await fetch("http://localhost:3000/api/movies")).json();
return {
props: {
results,
},
};
}
그러면 저 return된 props는 어디서 쓸 수 있냐하면, 현재 컴포넌트에 파라미터로 적어주면 된다.
export default function Home({ results })
여기서 잠깐 _app.js으로 가면 pageProps를 볼 수 있다. 우리가 return된 props를 사용하는 과정에서 이 pageProps가 사용된다.
순서대로 설명하면
1. 처음 앱이 실행되면 _app.js에서 Component에 index.js를 불러온다.
2. index.js에가면 getServerSideProps함수가 있다는걸 next가 감지하고 getServerSideProps를 실행후 return안에 있는 값들을 다시 _app.js로 가져온다.
3. 이 가져온 값들이 pageProps에 저장되어있기 때문에 index.js는 pageProps의 값을 파라미터로 전달 받아서 그것을 사용할 수 있게 된다.
그리고 map 사용하는 부분도 results로 변경해준다.
웹페이지에 페이지소스보기를 누르면 이제 영화데이터의 정보를 볼 수 있다. 그러나 이 함수를 사용하면 데이터가 패치되기 전까지는 아무것도 보이지 않는다.
그렇기 때문에 로딩이 보이고 데이터를 띄울 것인지, 아니면 SSR을 사용하기 위해서 로딩을 사용하지 않을 것인지 선택해서 사용하면 된다.
index.js 전체코드
import Seo from "../components/Seo";
export default function Home({ results }) {
return (
<div className="container">
<Seo title="Home" />
{results?.map((movie) => (
<div className="movie" key={movie.id}>
<img src={`https://image.tmdb.org/t/p/w500/${movie.poster_path}`} />
<h4>{movie.original_title}</h4>
</div>
))}
<style jsx>{`
.container {
display: grid;
grid-template-columns: 1fr 1fr;
padding: 20px;
gap: 20px;
}
.movie img {
max-width: 100%;
border-radius: 12px;
transition: transform 0.2s ease-in-out;
box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;
}
.movie:hover img {
transform: scale(1.05) translateY(-10px);
}
.movie h4 {
font-size: 18px;
text-align: center;
}
`}</style>
</div>
);
}
export async function getServerSideProps() {
const { results } = await (await fetch("http://localhost:3000/api/movies")).json();
return {
props: {
results,
},
};
}
'개발 공부 > Next JS' 카테고리의 다른 글
[노마드코더] NextJS 시작하기 #5 (0) | 2022.04.10 |
---|---|
[노마드코더] NextJS 시작하기 #3 (0) | 2022.04.08 |
[노마드코더] NextJS 시작하기 #2 (0) | 2022.04.07 |
[노마드코더] NextJS 시작하기 #1 (0) | 2022.03.22 |
[Next] NextJS의 getInitialProps, getStaticProps, getStaticPaths, getServerSideProps 함수 (0) | 2022.03.20 |