Search
moon
sun

State

State (상태)

💡
컴포넌트가 관리하는 로컬(내부) 데이터
React 컴포넌트의 상태(State)는 컴포넌트가 관리하는 로컬 데이터입니다. 상태를 사용하면 컴포넌트가 동적인 데이터를 저장하고 표시할 수 있으며, 상태가 변경될 때마다 React는 자동으로 화면을 다시 렌더링합니다.

State 의 특징

1.
상태 초기화 : 컴포넌트가 생성될 때 useState 훅이나 클래스 컴포넌트의 constructor 메서드에서 초기 상태를 설정합니다.
2.
상태 업데이트 : setCountthis.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단계

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단계

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단계

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
복사