티스토리 뷰
Reference
이 프로젝트는 코딩애플 리액트 강의를 참고하여 제작되었습니다.
https://codingapple.com/course/react-basic/
React 리액트 기초부터 쇼핑몰 프로젝트까지!
Next.js는 프론트엔드부터 서버까지 만들 수 있는 React기반 프레임워크입니다. 이것만 사용해도 풀스택 웹개발이 가능합니다. Next.js 사용시 서버사이드 렌더링이 쉽기 때문에 React, Vue만 사
codingapple.com
디테일페이지 데이터 바인딩 합니다.
디테일 컴포넌트도 데이터를 사용해야 합니다.
라우트로 데이터를 전달합니다.
<Route path="/detail" element={<Detail shoes={shoes} />} />
Detail.jsx
프롭스 받습니다.
export default function Detail(props)
일단 테스트로 0번 인덱스만 데이터 바인딩 해봅니다.
<h4 className="pt-5">{props.shoes[0].title}</h4>
<p>{props.shoes[0].content}</p>
<p>{props.shoes[0].price}</p>
TEST 진행
http://localhost:3000/detail/
뷰 체크 합니다.
아이디유알엘 뚫어 봅니다.
App.jsx
디테일페이지는 쇼핑몰콘셉트이므로 당연히 많은 페이지가 아이디로 생성되어야 합니다.
id를 뚫어 봅니다.
/detail/:id
세팅한 패스 뒤에 이렇게 /:id를 붙이면 무조건 앞패스에 해당하는 페이지가 렌더링 됩니다.
즉, 아이디 자리에 어떤 넘버가 붙어도 앞패스가 렌더링 됩니다.
리액트가 그렇게 하면 된다고 합니다.
못 믿겠다면 아무 숫자나 붙여서 테스트 해 봅니다.
<Route path="/detail/:id" element={<Detail shoes={shoes} />} />
TEST 진행
http://localhost:3000/detail/555
해당 아이디가 렌더링 된다면 기분이 살짝 좋아질 것 같습니다.
개선 합니다.
아이디유알엘로 디테일페이지를 렌더링 해 봅니다.
Detail.jsx
유즈파람스라는 친구를 임포트 합니다.
이것은 브라우저에 꽂히는 유알엘을 파라미터로 땡겨올수 있는 해리포터 같은 친구입니다.
import { useParams } from "react-router-dom";
리턴 위에 아이디데이터를 유즈파람스로 초기화 합니다.
let { id } = useParams();
데이터에 id를 찍어 줍니다.
이렇게 하면 유알엘 아이디가 이 코드 아이디에 찍힙니다.
그렇다면 당연히 아이디에 해당하는 데이터가 바인딩 되어야 할것입니다.
<h4 className="pt-5">{props.shoes[id].title}</h4>
<p>{props.shoes[id].content}</p>
<p>{props.shoes[id].price}</p>
살짝 궁금하니까 아이디변수 아래에 체크해 봅니다.
console.log(id);
뷰는 이상할 수 있지만 괜찮습니다.
http://localhost:3000/detail/222
라고 렌더링 하고 콘솔 체크 합니다.
test
뷰 체크 합니다.
http://localhost:3000/detail/0
http://localhost:3000/detail/1
http://localhost:3000/detail/2
콘솔에 아이디가 잘 찍힐것입니다.
워워 이미지가 안바뀐다고 놀라지 마세요.
이미지는 아직 처리 안했으니 텍스트 데이터만 체크합니다.
그런데 잘 생각해 보니 유알엘에 데이터 어레이 아이디를 사용하면 더 멋질것 같습니다.
개선합니다.
데이터 아이디와 유알엘 아이디를 매칭시켜 봅니다.
Deatil.jsx
유즈파람스로 얻은 아이디와 일치하는 아이디를 프롭스로 받은 데이터 어레이에서 찾습니다.
일단 아이디 콘솔 아래에 데이터아이디를 초기화 합니다.
유알엘 아이디를 상품데이터 아이디와 매칭시킵니다.
find() - 어레이에서 파라미터에 입력된 값을 찾아주는 JS 메서드 입니다.
find()는 어레이 중 해당데이터에서 첫번째 요소를 찾으면 임무를 종료 합니다.
x는 데이터를 받기위한 그냥 펑션세팅입니다.
어레이에서 아이디를 찾습니다.
그 아이디를 리턴합니다.
그 아이디를 데이터아이디로 초기화 합니다.
사실 이 데이터아이디는 shoes[id]가 모두 담겨 있습니다.
let dataId = props.shoes.find(function (x) {
return x.id == id;
});
코드 체지방을 살짝 줄입니다.
이퀄은 두개만 사용 합니다.
여기서 만약 이퀄을 3개 쓰고 싶다면 넘버타입으로 파싱 해야 합니다.
let dataId = props.shoes.find((x) => x.id === Number(id));
왜냐하면 유알엘 아이디는 스트링이기 때문입니다.
JS도 타입에 대해 어느 정도는 엄격합니다. 쿨럭...
let dataId = props.shoes.find((x) => x.id == id);
// Detail 컴포넌트를 정의하고 props를 인자로 받습니다.
export default function Detail(props) {
// useParams를 사용하여 URL 매개변수를 추출합니다. 예를 들어, URL이 "/detail/1"이라면, id 변수에는 "1"이 할당됩니다.
let { id } = useParams();
// props로 전달받은 신발(shoes) 배열에서 URL 매개변수 id와 일치하는 id를 가진 항목을 찾습니다.
// find() 메서드는 주어진 판별 함수를 만족하는 배열의 첫 번째 요소를 반환합니다.
// 이 코드는 URL 파라미터 id(문자열)와 배열 요소의 id(숫자)를 비교하므로, 암묵적 타입 변환(type coercion)이 발생할 수 있습니다.
let dataId = props.shoes.find((x) => x.id == id);
이미지 데이터 바인딩 합니다.
데이터 아이디가 0부터 시작하니까 1을 살짝 플러스 해주는 센스도 잊지 않습니다.
<img
src={
'https://raw.githubusercontent.com/lshjju/cdn/refs/heads/main/ca-shop/s' +
(dataId.id + 1) +
'.PNG'
}
width="100%"
/>
텍스트 데이터 바인딩 합니다.
프롭스는 저 위에 데이터 아이디가 했으니 이렇게 해도 됩니다.
<h4 className="pt-5">{dataId.title}</h4>
<p>{dataId.content}</p>
<p>{dataId.price}</p>
TEST 진
뷰를 체크 합니다.
뷰로 달라 보이는 것은 없습니다.
뷰만 잘 작동하면 성공입니다.
http://localhost:3000/detail/0
http://localhost:3000/detail/1
http://localhost:3000/detail/2
Completion
Detail.jsx
import { useParams } from 'react-router-dom';
export default function Detail(props) {
let { id } = useParams();
console.log(id);
let dataId = props.shoes.find((x) => x.id == id);
return (
<div className="container">
<div className="row">
<div className="col-md-6">
<img
src={
'https://raw.githubusercontent.com/lshjju/cdn/refs/heads/main/ca-shop/s' +
(dataId.id + 1) +
'.PNG'
}
width="100%"
/>
</div>
<div className="col-md-6">
<h4 className="pt-5">{dataId.title}</h4>
<p>{dataId.content}</p>
<p>{dataId.price}</p>
<button className="btn btn-danger">order</button>
</div>
</div>
</div>
);
}
App.jsx
import { useState } from 'react';
import reactLogo from './assets/react.svg';
import viteLogo from '/vite.svg';
import './App.css';
import 'bootstrap/dist/css/bootstrap.min.css';
import { Routes, Route, Link, Outlet } from 'react-router-dom';
import data from './data.jsx';
import Detail from './routes/Detail';
export default function App() {
let [shoes] = useState(data);
return (
<div className="App">
<nav class="navbar navbar-expand-lg bg-body-tertiary">
<div class="container-fluid">
<a class="navbar-brand" href="#">
Navbar
</a>
<button
class="navbar-toggler"
type="button"
data-bs-toggle="collapse"
data-bs-target="#navbarNavAltMarkup"
aria-controls="navbarNavAltMarkup"
aria-expanded="false"
aria-label="Toggle navigation"
>
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
<div class="navbar-nav">
<Link to="/" class="nav-link active" aria-current="page">
Home
</Link>
<Link to="/detail" class="nav-link active" aria-current="page">
Detail
</Link>
<Link
to="/company/manpower"
class="nav-link active"
aria-current="page"
>
Manpower
</Link>
<Link
to="/company/map"
class="nav-link active"
aria-current="page"
>
Map
</Link>
</div>
</div>
</div>
</nav>
<Routes>
<Route
path="/"
element={
<>
<div className="main-bg"></div>
<h1 className="my-5">Nike shop</h1>
<div className="d-flex flex-column mb-3">
{shoes.map((shoe, i) => {
return <Goods shoes={shoe} i={i}></Goods>;
})}
</div>
</>
}
/>
<Route path="/detail/:id" element={<Detail shoes={shoes} />} />
<Route path="/company" element={<Company />}>
<Route path="manpower" element={<Manpower />} />
<Route path="map" element={<Map />} />
</Route>
<Route path="*" element={<Nopage />} />
</Routes>
<div class="card m-5">
<div class="card-header">Featured</div>
<div class="card-body">
<h5 class="card-title">Special title treatment</h5>
<p class="card-text">
With supporting text below as a natural lead-in to additional
content.
</p>
<a href="#" class="btn btn-primary">
Go somewhere
</a>
</div>
</div>
</div>
);
}
function Goods(props) {
return (
<div className="p-2">
<img
src={
'https://raw.githubusercontent.com/lshjju/cdn/refs/heads/main/ca-shop/s' +
(props.i + 1) +
'.PNG'
}
width="80%"
/>
<h4 className="my-3">{props.shoes.title}</h4>
<p>{props.shoes.price}</p>
</div>
);
}
function Company() {
return (
<div>
<h4 className="my-3">company</h4>
<p>It's a company</p>
<Outlet></Outlet>
</div>
);
}
function Manpower() {
return (
<div>
<img
src="https://plus.unsplash.com/premium_photo-1688821131205-52f5c633ce69?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
width="80%"
/>
</div>
);
}
function Map() {
return (
<div>
<img
src="https://images.unsplash.com/photo-1548345680-f5475ea5df84?q=80&w=2073&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
width="80%"
/>
</div>
);
}
function Nopage() {
return (
<div>
<h4 className="my-2">No page</h4>
<p>hmmm....</p>
<img
src="https://cdn.maily.so/ixmvzk5qh83mee5kcjw8pp55fihe"
width="80%"
/>
</div>
);
}
참고사이트 : ca05
ca05
Reference이 프로젝트는 코딩애플 리액트 강의를 참고하여 제작되었습니다.https://codingapple.com/course/react-basic/ React 리액트 기초부터 쇼핑몰 프로젝트까지! Next.js는 프론트엔드부터 서버까지 만들 수
lshjju.tistory.com
'프로그래밍언어 > 리엑트' 카테고리의 다른 글
| [React] LifeCycle (0) | 2026.03.21 |
|---|---|
| [React] 프로젝트 만들기 part6 (1) | 2026.03.21 |
| [React] 프로젝트 만들기 part4 (0) | 2026.03.21 |
| [React] 프로젝트 만들기 part3 (0) | 2026.03.21 |
| [React] 프로젝트 만들기 part2 (0) | 2026.03.21 |
