State (상태)
컴포넌트가 관리하는 로컬(내부) 데이터
React 컴포넌트의 상태(State)는 컴포넌트가 관리하는 로컬 데이터입니다. 상태를 사용하면 컴포넌트가 동적인 데이터를 저장하고 표시할 수 있으며, 상태가 변경될 때마다 React는 자동으로 화면을 다시 렌더링합니다.
State 의 특징
1.
상태 초기화
: 컴포넌트가 생성될 때 useState 훅이나 클래스 컴포넌트의 constructor 메서드에서 초기 상태를 설정합니다.
2.
상태 업데이트
: setCount나 this.setState를 사용하여 상태를 업데이트합니다. 이때 이전 상태를 참조하여 변경을 진행하는 것이 중요합니다.
3.
컴포넌트별로 고유한 상태
: 각각의 인스턴스는 자체 상태를 유지하므로, 동일한 컴포넌트의 여러 인스턴스 간에는 각각 독립적인 상태가 존재합니다.
실습 코드
“상품 상세 UI 만들기 - state”
“상품 상세 UI 만들기 - state” 에서는 1~3단계로 나누어 실습합니다.
1단계 - JSX 문법만을 사용하여 ProductDetail.jsx 컴포넌트를 정적 컨텐츠로 디자인합니다.
2단계 - JavaScript 객체를 사용하여, 동적으로 상품 상세 정보를 랜더링 합니다.
3단계 - quantity (수량) 을 상태(state) 로 정의하여,
,
버튼을 눌러 이벤트 처리를 하고, 상태변화에 따라 리랜더링하는 과정을 확인합니다.
,
버튼을 눌러 이벤트 처리를 하고, 상태변화에 따라 리랜더링하는 과정을 확인합니다.작업할 파일
•
componets/ProductDetail.jsx
•
App.js
•
App.css
1단계
JSX 문법만을 사용하여 ProductDetail.jsx 컴포넌트를 정적 컨텐츠로 디자인합니다
•
componets/ProductDetail.jsx
•
App.js
•
App.css
컴포넌트 구조
•
App
◦
ProductDetail
App, ProductDetail 컴포넌트
componets/ProductDetail.jsx
import React from 'react'
const ProductDetail = () => {
return (
<div className='product-detail'>
<div className="item img">
<img src="https://i.imgur.com/1vpSkbW.png" alt="상품명" />
</div>
<div className="item info">
<div className="title">
<h1>상품명</h1>
</div>
<p>
<span className='txt-pt'>INFO</span><br />
-편안한 착용이 가능한 폴라 디자인 <br />
-체형 커버가 가능한 베이직한 실루엣 <br />
</p>
<p>
<span className="txt-pt">Color & Size</span> <br />
Black, Navy, Red <br />
85, 90, 95, 100, 110 <br />
- 어깨 53, 가슴 59, 암홀 23, 소매 62, 총장 68 <br />
(측정 방법에 따라 1~3cm 오차가 있을 수 있습니다) <br />
</p>
<span className='line-lg'></span>
<div className="text-group">
<div className="item">
<span className='txt-pt'>판매가</span>
</div>
<div className="item">
<span className='txt-pt'>42,000 원</span>
</div>
</div>
<div className="text-group">
<div className="item">
<span>배송비</span>
</div>
<div className="item">
<span>3,000</span> 원
</div>
</div>
<span className="line-lg"></span>
<div className="text-group">
<div className="item">
<span>Color</span>
</div>
<div className="item">
<select name="color" id="color">
<option value="Black">Black</option>
<option value="Navy">Navy</option>
<option value="Red">Red</option>
</select>
</div>
</div>
<div className="text-group">
<div className="item">
<span>Size</span>
</div>
<div className="item">
<select name="color" id="color">
<option value="85">85</option>
<option value="90">90</option>
<option value="95">95</option>
<option value="100">100</option>
<option value="110">110</option>
</select>
</div>
</div>
<div className="text-group">
<div className="item">
<span>수량</span>
</div>
<div className="item flex">
<input type="text" className='quantity' min={1} max={100} value={1} />
<button className='btn btn-xs'>+</button>
<button className='btn btn-xs'>-</button>
</div>
</div>
<span className="line-lg"></span>
<div className="text-group">
<div className="item">
<span className='txt-pt'>최종 가격</span>
</div>
<div className="item">
<span className='txt-pt'>42,000</span>
</div>
</div>
<div className="text-group flex gap-1">
<div className="item">
<button className="btn btn-lg">구매하기</button>
</div>
<div className="item flex">
<button className="btn btn-lg btn-outline">장바구니</button>
<button className="btn btn-lg btn-outline">관심상품</button>
</div>
</div>
</div>
</div>
)
}
export default ProductDetail
JavaScript
복사
App.js
import logo from './logo.svg';
import './App.css';
import ProductDetail from './components/ProductDetail';
function App() {
return (
<div>
<ProductDetail />
</div>
);
}
export default App;
JavaScript
복사
App.css
.txt-pt {
color: salmon;
font-weight: bold;
}
.line-lg {
display: block;
border-bottom: 2px solid lightgray;
}
.product-detail {
display: inline-flex;
justify-content: center;
column-gap: 100px;
padding: 10px;
/* border: 1px solid salmon; */
}
.product-detail .item {
width: 400px;
}
.product-detail .info {
border-top: 2px solid black;
border-bottom: 2px solid black;
}
.quantity {
width: 40px;
font-size: 16px;
line-height: 20px;
height: 20px;
text-align: center;
appearance: none;
}
/* 공통 */
.text-group {
display: flex;
padding: 10px 0;
}
img { width: 100%; }
select {
width: 100%;
font-size: 16px;
line-height: 40px;
height: 40px;
padding-left: 15px;
padding-right: 15px;
color: #222;
color: inherit;
cursor: pointer;
outline: none;
border: 1px solid #777;
background: none;
border-radius: 0;
padding-right: 32px;
}
.flex {
display: flex;
}
.flex.gap-1 {
gap: 10px;
}
.btn {
background-color: royalblue;
color: white;
font-weight: bold;
outline: none;
border: none;
margin: 0 2px;
padding: 0 10px;
cursor: pointer;
}
.btn:hover {
box-shadow: 2px 2px 2px rgba(0,0,0,0.5);
}
.btn:active {
box-shadow: none;
}
.btn.btn-lg {
width: 100%;
min-height: 40px;
}
.btn.btn-outline {
border: 2px solid gray;
background-color: white;
color: gray;
}
CSS
복사
2단계
JavaScript 객체를 사용하여, 동적으로 상품 상세 정보를 랜더링 합니다.
•
componets/ProductDetail.jsx
컴포넌트 구조
•
App
◦
ProductDetail
App, ProductDetail 컴포넌트
componets/ProductDetail.jsx
import React, { useState } from 'react'
const ProductDetail = (props) => {
// ✅ 객체 추가
const product = {
productId : 'p000001',
name : '베이직 폴라 니트',
price : 42000,
quantity : 1,
img: 'https://i.imgur.com/1vpSkbW.png',
}
// ✅ 최종 가격 계산
const totalPrice = product.price * product.quantity
return (
<div className='product-detail'>
<div className="item img">
<img src={product.img} alt={product.name} />
</div>
<div className="item info">
<div className="title">
<h1>{product.name}</h1>
</div>
<p>
<span className='txt-pt'>INFO</span><br />
-편안한 착용이 가능한 폴라 디자인 <br />
-체형 커버가 가능한 베이직한 실루엣 <br />
</p>
<p>
<span className="txt-pt">Color & Size</span> <br />
Black, Navy, Red <br />
85, 90, 95, 100, 110 <br />
- 어깨 53, 가슴 59, 암홀 23, 소매 62, 총장 68 <br />
(측정 방법에 따라 1~3cm 오차가 있을 수 있습니다) <br />
</p>
<span className='line-lg'></span>
<div className="text-group">
<div className="item">
<span className='txt-pt'>판매가</span>
</div>
<div className="item">
<span className='txt-pt'>{product.price.toLocaleString()} 원</span>
</div>
</div>
<div className="text-group">
<div className="item">
<span>배송비</span>
</div>
<div className="item">
<span>3,000</span> 원
</div>
</div>
<span className="line-lg"></span>
<div className="text-group">
<div className="item">
<span>Color</span>
</div>
<div className="item">
<select name="color" id="color">
<option value="Black">Black</option>
<option value="Navy">Navy</option>
<option value="Red">Red</option>
</select>
</div>
</div>
<div className="text-group">
<div className="item">
<span>Size</span>
</div>
<div className="item">
<select name="color" id="color">
<option value="85">85</option>
<option value="90">90</option>
<option value="95">95</option>
<option value="100">100</option>
<option value="110">110</option>
</select>
</div>
</div>
<div className="text-group">
<div className="item">
<span>수량</span>
</div>
<div className="item flex">
<input type="text" className='quantity' value={product.quantity} min={1} max={100} />
<button className='btn btn-xs'>+</button>
<button className='btn btn-xs'>-</button>
</div>
</div>
<span className="line-lg"></span>
<div className="text-group">
<div className="item">
<span className='txt-pt'>최종 가격</span>
</div>
<div className="item">
<span className='txt-pt'>{totalPrice.toLocaleString()} 원</span>
</div>
</div>
<div className="text-group flex gap-1">
<div className="item">
<button className="btn btn-lg">구매하기</button>
</div>
<div className="item flex">
<button className="btn btn-lg btn-outline">장바구니</button>
<button className="btn btn-lg btn-outline">관심상품</button>
</div>
</div>
</div>
</div>
)
}
export default ProductDetail
JavaScript
복사
3단계
quantity (수량) 을 상태(state) 로 정의하여,
,
버튼을 눌러 이벤트 처리를 하고, 상태변화에 따라 리랜더링하는 과정을 확인합니다.
,
버튼을 눌러 이벤트 처리를 하고, 상태변화에 따라 리랜더링하는 과정을 확인합니다.•
componets/ProductDetail.jsx
컴포넌트 구조
•
App
◦
ProductDetail
App, ProductDetail 컴포넌트
componets/ProductDetail.jsx
import React, { useState } from 'react'
const ProductDetail = (props) => {
// ✅ state 추가
const [quantity, setQuantity] = useState(1)
// ✅ 객체 추가
const product = {
productId : 'p000001',
name : '베이직 폴라 니트',
price : 42000,
quantity : 1,
img: 'https://i.imgur.com/1vpSkbW.png',
}
// ✅ 최종 가격 계산
const totalPrice = product.price * quantity
// ✅ 이벤트 정의 - 증가
const onIncrease = () => {
console.log(quantity);
setQuantity(quantity + 1)
}
// ✅ 이벤트 정의 - 감소
const onDecrease = () => {
console.log(quantity);
setQuantity(quantity - 1)
}
return (
<div className='product-detail'>
<div className="item img">
<img src={product.img} alt={product.name} />
</div>
<div className="item info">
<div className="title">
<h1>{product.name}</h1>
</div>
<p>
<span className='txt-pt'>INFO</span><br />
-편안한 착용이 가능한 폴라 디자인 <br />
-체형 커버가 가능한 베이직한 실루엣 <br />
</p>
<p>
<span className="txt-pt">Color & Size</span> <br />
Black, Navy, Red <br />
85, 90, 95, 100, 110 <br />
- 어깨 53, 가슴 59, 암홀 23, 소매 62, 총장 68 <br />
(측정 방법에 따라 1~3cm 오차가 있을 수 있습니다) <br />
</p>
<span className='line-lg'></span>
<div className="text-group">
<div className="item">
<span className='txt-pt'>판매가</span>
</div>
<div className="item">
<span className='txt-pt'>{product.price.toLocaleString()} 원</span>
</div>
</div>
<div className="text-group">
<div className="item">
<span>배송비</span>
</div>
<div className="item">
<span>3,000</span> 원
</div>
</div>
<span className="line-lg"></span>
<div className="text-group">
<div className="item">
<span>Color</span>
</div>
<div className="item">
<select name="color" id="color">
<option value="Black">Black</option>
<option value="Navy">Navy</option>
<option value="Red">Red</option>
</select>
</div>
</div>
<div className="text-group">
<div className="item">
<span>Size</span>
</div>
<div className="item">
<select name="color" id="color">
<option value="85">85</option>
<option value="90">90</option>
<option value="95">95</option>
<option value="100">100</option>
<option value="110">110</option>
</select>
</div>
</div>
<div className="text-group">
<div className="item">
<span>수량</span>
</div>
<div className="item flex">
<input type="text" className='quantity' value={quantity} min={1} max={100} /> {/* value 를 state 로 변경 */}
<button className='btn btn-xs' onClick={ onIncrease }>+</button> {/* 이벤트 추가 */}
<button className='btn btn-xs' onClick={ onDecrease }>-</button> {/* 이벤트 추가 */}
</div>
</div>
<span className="line-lg"></span>
<div className="text-group">
<div className="item">
<span className='txt-pt'>최종 가격</span>
</div>
<div className="item">
<span className='txt-pt'>{totalPrice.toLocaleString()} 원</span>
</div>
</div>
<div className="text-group flex gap-1">
<div className="item">
<button className="btn btn-lg">구매하기</button>
</div>
<div className="item flex">
<button className="btn btn-lg btn-outline">장바구니</button>
<button className="btn btn-lg btn-outline">관심상품</button>
</div>
</div>
</div>
</div>
)
}
export default ProductDetail
JavaScript
복사













