만약 input태그에 뭔가 입력시 코드를 실행하고 싶다면
onChange/ onInput 을 작성하면 된다.
그래서 이벤트핸들러(onClick, onChange, onInput …) 종류는 굉장히 많다.
여기서 input태그 내부의 입력값을 가져오고 싶을 경우 함수의 파라미터값을 가져와서 사용
// onchange가 감지할 때마다 생성되는 e
<input onchange={(e)=> { console.log(e) }}/>
// onchange가 감지할 때마다 생성되는 이벤트가 발생한 html태그
<input onchange={(e)=> { console.log(e.tartget) }}/>
// onchange가 감지할 때마다 생성되는 이벤트가 발생한 html태그에 입력한 값
<input onchange={(e)=> { console.log(e.target.value) }}/>
그리고 현재 강의대로 작성했다면 아래의 코드가 작성이 되어있을텐데
그렇다면 span 태그에 있는 따봉을 눌러도 모달창이 뜨게된다.
이유는 클릭 이벤트는 상위 html로 퍼지게 되는데 이걸 이벤트 버블링이라고 부른다.
{
글제목.map((article,i)=> {
return (
<div>
<h1 onClick={()=> {
let title = article
선택글제목변경(title)
setModal(true)
}}>{article} <span style={{cursor: 'pointer'}} onClick={()=> {
let thumb = [...따봉]
thumb[i] = 따봉[i] +1
따봉변경(thumb)
}}>👍</span>{따봉[i]}</h1>
<div>{글제목2[i]}</div>
</div>
)
})
}
그래서 이벤트 버블링 현상을 막고 싶다면 하위태그에서 클릭 이벤트시 stopPropagation을 넣어주기.
{
글제목.map((article,i)=> {
return (
<div>
<h1 onClick={()=> {
let title = article
선택글제목변경(title)
setModal(true)
}}>{article} <span style={{cursor: 'pointer'}} onClick={(e)=> {
e.stopPropagation()
let thumb = [...따봉]
thumb[i] = 따봉[i] +1
따봉변경(thumb)
}}>👍</span>{따봉[i]}</h1>
<div>{글제목2[i]}</div>
</div>
)
})
}
그런데 console.loe()로 확인을 해보게 되면 늦게 처리된다.
이유는 state변경함수는 늦게처리되는데 이것을 비동기처리라고 한다.
그래서 빠르게 처리될 수 있는 console.log()부터 처리한 뒤 state변경 함수를 처리하게 되서어 늦게 변경된다.
숙제.
- 버튼을 누르면 글 하나 추가되는 기능 만들기
- 우선 내가한 방식은 강사님이 말씀해주신것처럼 기존에 정의해둔 글제목 state를 변수에 담아줌과 동시에 가장 앞에 e.target.value를 추가하여 담아주고 state 변경하기 위한 함수로 글제목함수에 해당 변수를 넣어주어 해결
- 추가> 강사님이 하는 방법은
- title.unshift(입력값) 로 unshift() 메서드를 통해 추가
- 추가> 강사님이 하는 방법은
<input onChange={(e)=> { 입력값변경(e.target.value) }}/> <button onClick={()=> { let title = [입력값,...글제목] 글제목함수(title) }}>저장</button>
- 우선 내가한 방식은 강사님이 말씀해주신것처럼 기존에 정의해둔 글제목 state를 변수에 담아줌과 동시에 가장 앞에 e.target.value를 추가하여 담아주고 state 변경하기 위한 함수로 글제목함수에 해당 변수를 넣어주어 해결
- 글마다 삭제버튼 & 삭제 하기
- 고민을 조금했는데 그냥 filter() 메서드를 사용하여 현재 map() 메서드로 돌아서 얻게되는 article 값이 있기 때문에 해당 값과 같지 않은 것들을 변수에 담아주도록 하고 글제목함수를 통해 state변경해주기
- 추가> 강사님이 하는 방법은
- title.splice(i, 1)로 splice() 메서드로 삭제했음
- 추가> 강사님이 하는 방법은
{ 글제목.map((article,i)=> { return ( <div> <h1 onClick={()=> { let title = article 선택글제목변경(title) setModal(true) }}>{article} <span style={{cursor: 'pointer'}} onClick={(e)=> { e.stopPropagation() let thumb = [...따봉] thumb[i] = 따봉[i] +1 따봉변경(thumb) }}>👍</span>{따봉[i]}</h1> <div>{글제목2[i]} <button onClick={(e)=>{ let title = [...글제목].filter((t) => { return t != article }) 글제목함수(title) }}>삭제</button></div> </div> ) }) }
- 고민을 조금했는데 그냥 filter() 메서드를 사용하여 현재 map() 메서드로 돌아서 얻게되는 article 값이 있기 때문에 해당 값과 같지 않은 것들을 변수에 담아주도록 하고 글제목함수를 통해 state변경해주기
여기서 추가로 더 시도 해본것으로
- 작성 후 해당 input 태그의 value를 빈칸으로 초기화 하기
- DOM을 이용해서 id에 해당하는 input 태그를 지정후 해당 태그를 ‘’으로 변경
else { let title = [입력값,...글제목] let inp = document.getElementById('input') inp.value = '' 글제목함수(title) }
- 저장 버튼 클릭이 아니어도 Enter 버튼을 누르게 되면 저장이 될 수 있도록 하기
- 우선 button 태그에서 많은 시도를 해봤는데 결국에는 input 태그로 해결을 했고
- input태그에서도 type을 submit으로 해서 onSubmit으로도 해보고 했지만 잘 안되었다.
- 그래서 결국 해결한 방법은 type을 없애고 onKeyDown의 event를 가져와서 해당 event의 key가 Enter의 경우 실행되도록 하여 해결
<input id="input" onChange={(e)=> { 입력값변경(e.target.value) console.log(입력값); }} onKeyDown={(e)=> { if (e.key === 'Enter') { console.log(e.key); if (입력값 === ''){ alert('경고') } else { let title = [입력값,...글제목] let inp = document.getElementById('input') inp.value = '' 글제목함수(title) } } }} />
- 그리고 입력칸이 빈칸일 경우 저장되지 않도록 하기
- 이것은 간단했다 그냥 현재 입력값이라고 하는 state의 값이 ‘’ 이렇게 비워져있다면 alert 처리를 해주는 것으로 해결
if (입력값 === ''){ alert('경고') }
클래스 문법으로 컴포넌트를 만드는 방법
우선 class는 변수, 함수 보관함으로 생각하면 된다.
- 리엑트에서는 우선적으로 채워줘야하는 것이 아래와 같다 (그냥 template처럼 생각하면 된다.)
- constructor
- super
- render
- 그래서 아래와 같이 작성하게 되면 Modal2를 작성하는 것만으로 긴 HTML을 작성할 수 있게 된다.
- class Modal2 extends React.Component { constructor() { super() } render() { return ( <div>안녕</div> ) } }
- 해당 클래스는 App 의 최상단 태그 안에 작성해야 작성이 된다.
- 그래서 현재 사용하는 Component와 Class를 통해 만든 Component 작성을 비교하면 아래와 같다
// 현재
function Modal(props) {
return (
<div className="modal" style={{backgroundColor:props.color}}>
<h2 id="title">{props.선택글제목}</h2>
<p>날짜</p>
<p>상세내용</p>
</div>
);
}
// class를 통해 만든 Component
class Modal2 extends React.Component {
constructor() {
super()
}
render() {
return (
<div>안녕</div>
)
}
}
- class를 통해 만든 컴포넌트에서의 state만드는 방법
class Modal2 extends React.Component {
constructor() {
super();
this.state= {
name: 'kim',
age: 20
}
}
render() {
return (
<div>안녕</div>
)
}
}
- state적용 꼭 가장 최상단 태그 안에 작성을 해야 한다.
class Modal2 extends React.Component {
constructor() {
super();
this.state= {
name: 'kim',
age: 20
}
}
render() {
return (
<div>
안녕 {this.state.name}
</div>
)
}
}
- 기존의 state 값을 변경하고자 할 경우 아래와 같이 this.setState({변수: 변경값}) 이렇게 변경해주면 된다.
class Modal2 extends React.Component {
constructor() {
super();
this.state= {
name: 'kim',
age: 20
}
}
render() {
return (
<div>
안녕 {this.state.name}
<button onClick={()=> {
this.setState({name:'hongmin'})
}}>버튼</button>
</div>
)
}
}
- 그리고 props를 하고자 할 경우엔
class Modal2 extends React.Component {
constructor(props) {
super(props);
this.state= {
name: 'kim',
age: 20
}
}
render() {
return (
<div>
안녕 {this.props}
<button onClick={()=> {
this.setState({name:'hongmin'})
}}>버튼</button>
</div>
)
}
}
font 적용
로컬에 다운로드한 폰트를 적용하는 방법
- react에 assets 폴더를 만든다
- font라는 폴더를 만들어 정리를 한번 더 해준다
// css 파일을 생성
@font-face {
font-family: '해당 폰트를 불러올 이름 작성(웬만하면 원래 이름 적어주기)'
src: url('현재 파일이 있는 경로')
}
// 적용하는 방법
font-family: '해당 폰트 이름'
새로운 리엑트 프로젝트 생성
기존의 blog 폴더가 있는 곳에서의 상위 폴더에서
shift + 마우스 우클릭 을 하게 되면 여기서 powerShell 창 열기가 나오게 되고 클릭하면 된다.
이후 PowerShell에서 npx create-react-app {프로젝트 이름} 을 작성하게 되면 프로젝트가 생성된다.
React에서 Bootstrap 사용하기
우선 구글에 react bootstrap을 입력하면 상단에 나오는 홈페이지에서 cdn같은 걸 복사 후 터미널에서 실행 시키고
페이지 하단에 있는 CSS 부분에서 App.js 에 넣을 import 도 함께 넣어주자.
이후 만약 버튼을 하나 넣으려고 할때
아래와 같은 코드만 넣어준다고 해서 되는 것이 아니라 component이기 때문에
react bootstrap 홈페이지에서 importing Components부분을 참고하여 import또한 같이 해줘야한다.
<Button variant="primary">Primary</Button>{' '}
/여기서 문제점/
리엑트에서 부트스트랩을 사용하려 할 경우 !important 때문인지 해당 태그 안에서 style을 먹이지 않으면 css가 먹지 않는 것 같다.
css파일 내에서 적용을 하려해도 안되는게 무슨 이유일까…?
background-image 속성
url() 주소를 넣어주어 해당 경로를 입력해주고
background-position 속성을 통해 해당 배경 사진의 위치를 변경할 수 있다.
그리고 css 파일 상에서는 위의 방법과 같이 작성해주면 되지만
App.js 즉 html에서 src폴더의 이미지를 넣을 땐
상단에 import {작명} from {이미지 경로} 와 같이 경로를 받아온후
style={{ backgroundImage:’url( ‘+작명+’ )’ }} 과 같이 해줘야한다.
주의할점
꼭 backgroundImage: ‘ url ( ’ + 작명 + ‘ ) ‘ 이렇게 해줘야한다는것
다음으로 하단에 3개의 사진을 넣어주기
궁금했던 부분: Q. 왜 그냥 bootstrap을 사용하지 않고 react bootstrap을 사용할까?
A. 조금 더 용량 절약이 가능하기 때문
html에서 img 파일을 불러올 때 위의 방식대로 하게 되면
너무 번거롭고 불편하기에
public 파일에 넣고 사용을 하기도 한다.
그래서 public 폴더에 있는 이미지를 사용할 경우
그냥 / 하나면 하고 바로 파일 명을 작성하면 사용이 가능하다
단 위의 방법을 사용할 경우 주의할 점
메인 페이지가 아닌 다른 경로에서 사용할 경우 조금 번거로워진다.
ex) <img src={process.env.PUBLIC_URL + ‘/img/logo,png’} />
DB가 아닌 그냥 현재 React에서 데이터를 넣은 후 값을 가져오고자 할 때
App.js 내부에서 하기에는 너무 코드가 길어지니까 다른 파일에서 불러오고자 할 경우!
- 먼저 src폴더 내부에 ex) data.js 파일을 생성 후
- 해당 파일에서 데이터를 export하고
- 만약 변수 a를 export 하려고 한다면 let a = 10 export default a
- App.js에서 import하면 끝
- App.js 파일에서 아래와 같이 코드를 작성하면 끝 import 작명 from './data.js'
여기서 만약에 여러 개를 export 시키려 한다면
// data/js
let a = 10
let b = 100
// 아래와 같이 중괄호로 감싸줘야한다.
export {a,b}
여기서 import를 하는 App.js에서는 아래와 같이 작성해야한다.
// App.js
import {a,b} from './data.js'
단 여러개를 export 했을 경우 import 할 경우 작명을 하나만 export 했을 때처럼 자유롭게 할 수 없고 export 했을 때의 변수 그대로 사용해야 한다.
그리고 함수,모달 또한 export 가능하다!!
과제
상품 목록 컴포넌트화
import {Navbar,Nav} from 'react-bootstrap';
let nav = [
<Navbar id='navbar' variant="dark">
<div className="grid subfont">
<div className=''></div>
<Nav style={{color:'black',justifyContent: 'center',alignItems: 'center' }} href="#home"><span class="material-symbols-outlined" style={{marginRight:'3px'}}>menu</span>메뉴</Nav>
<div></div>
<Nav style={{color:'black',justifyContent: 'center',alignItems: 'center' }} href="#features"><span class="material-symbols-outlined">search</span>검색</Nav>
<div></div>
<Nav className='mainfont' style={{color:'black',justifyContent: 'center',alignItems: 'center', fontSize:'2rem', letterSpacing:'5px'}} href="#home" >LOUIS VUITTON</Nav>
<div></div>
<Nav style={{color:'black',justifyContent: 'center',alignItems: 'center' }} href="#home" >위시리스트</Nav>
<div></div>
<Nav style={{color:'black',justifyContent: 'center',alignItems: 'center' }} href="#pricing">My lv</Nav>
<div></div>
<Nav style={{color:'black',justifyContent: 'center',alignItems: 'center' }} href="#pricing"><span class="material-symbols-outlined">shopping_cart_checkout</span></Nav>
</div>
</Navbar>
]
상품명 데이터 바인딩도 잘 해오기
let data = [
{
id : 0,
title : "White and Black",
content : "Born in France",
price : 120000
},
{
id : 1,
title : "Red Knit",
content : "Born in Seoul",
price : 110000
},
{
id : 2,
title : "Grey Yordan",
content : "Born in the States",
price : 130000
}
]
let items = [
<div style={{marginRight:'4px'}}>
<img className='items' src="https://images.pexels.com/photos/4602025/pexels-photo-4602025.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2" alt="" />
<p style={{marginTop:'7px'}}>{data[0].title}</p>
<p>{data[0].content}</p>
</div>,
<div style={{marginRight:'4px', marginLeft:'2px'}}>
<img className='items' src="https://images.pexels.com/photos/4276653/pexels-photo-4276653.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2" alt="" />
<p style={{marginTop:'7px'}}>{data[1].title}</p>
<p>{data[1].content}</p>
</div>,
<div style={{marginLeft:'4px'}}>
<img className='items' src="https://images.pexels.com/photos/4061385/pexels-photo-4061385.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2" alt="" />
<p style={{marginTop:'7px'}}>{data[2].title}</p>
<p>{data[2].content}</p>
</div>
]
반복적인 부분은 map 반복문 써보기
- array helper method의 경우엔 map을 예시로 들어본다면
- array.map() 이렇게 그냥 배열 형태인 곳 어디에든 사용하면 되는거 같다.
<div className='contain'>
<div></div>
{item.map((item) => {
return item
})}
<div></div>
</div>
위와 같이 data를 export 하고 import 하여 App.js에서 사용하다 보니 엄청 깔끔 해졌다.
import './App.css';
import React ,{useState} from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import {nav, items} from './data.js'
function App() {
let [item, setItems] = useState(items)
return (
<div className="App">
{/* navbar */}
{nav}
<div className='main-bg'>
{/* 배경사진 */}
</div>
<div className='contain'>
<div></div>
{item.map((item) => {
return item
})}
<div></div>
</div>
</div>
);
}
export default App;
props 다시 한번 정리
// component에 data를 props 시키려고 할 때
<component data={data}> </compoenent>
강사님이 의도한 거??

-------------------------------------------------------------------------------------
// 강사님이 의도한 바는 조금 다른거 같다
// App.js 내에서 아래의 코드를 작성해서 컴포넌트를 만드는 것으로 의도 하셨다.
function Card() {
return (
<div style={{marginRight:'4px'}}>
<img className='items' src="https://images.pexels.com/photos/4602025/pexels-photo-4602025.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2" alt="" />
<p style={{marginTop:'7px'}}>{data[0].title}</p>
<p>{data[0].content}</p>
</div>
)
}
// 이후 현재 Card 라는 컴포넌트 내부엔 datas 라는 데이터가 없기 때문에
// App컴포넌트에서 자식 컴포넌트인 Card 컴포넌트에게 props를 해줘야해서
// App컴포넌트 태그 안에 Card 컴포넌트 생성 후 props 해주기
<div>
<Card data={data}></Card>
</div>
// 다만 여러개의 컴포넌트를 가져올 예정이기에 위의 코드로 3개를 만든다면 아래처럼 될텐데
<div>
<Card data={data}></Card>
<Card data={data}></Card>
<Card data={data}></Card>
</div>
// 이렇게 되면 같은 data[0] 의 데이터만 조회하게 되기 때문에
<div>
<Card data={data[0]}></Card>
<Card data={data[1]}></Card>
<Card data={data[2]}></Card>
</div>
function Card() {
return (
<div style={{marginRight:'4px'}}>
<img className='items' src="https://images.pexels.com/photos/4602025/pexels-photo-4602025.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2" alt="" />
<p style={{marginTop:'7px'}}>{data.title}</p>
<p>{data.content}</p>
</div>
)
}
// 이렇게 작성해주면 각자 다른 data의 title과 content가 조회된다.
// 다만 이미지가 서로 다르게 되는데 이때 만약 서로 유사한 이미지 주소 혹은 파일명의 경우엔
// props로 해당 데이터에 맞는 숫자를 함께 props 해주고 데이터를 변경해주면 된다.
// 예를 들어 이미지 주소가 아래와 같을 경우
// https://codingapple1.github.io/shop/shoes1.jpg
// https://codingapple1.github.io/shop/shoes2.jpg
// https://codingapple1.github.io/shop/shoes3.jpg
// props 할때 함께 숫자도 넘겨주기
<div>
<Card data={data[0]} i={1}></Card>
<Card data={data[1]} i={2}></Card>
<Card data={data[2]} i={3}></Card>
</div>
// 이후 주소 또한 함께 변경하자면
// 먼저 src속성을 자바스크립트 입력란으로 변경해주고 {} => 문자열로 변경 '' 이후
// props 할 부분과 문자열인 부분을 나누어 + + 처리 해주어 처리 (문자 중간 변수 넣기 문법)
function Card() {
return (
<div style={{marginRight:'4px'}}>
<img className='items'
src={'https://codingapple1.github.io/shop/shoes' + props.i + '.jpg'} alt="" />
<p style={{marginTop:'7px'}}>{data.title}</p>
<p>{data.content}</p>
</div>
)
}
// 그리고 map()함수를 이용해서 컴포넌트를 보이도록 하는 방법
// 위와 같이 작성하게 되면 계속 변화하는 변수의 경우엔
// 시시각각으로 변경을 해야하기에 손이 많이 간다 그래서 반복문을 통해 다 보여주도록 하기
// map()함수를 작성할 때는 {}중괄호 안에다가 작성하기
// 우선 사진의 경우엔 지금은 조금 맞지는 않지만 이렇게도 가능하다는 것!
{
data.map((data, idx)=> {
return (
<Card data={data} i={4602025-idx}></Card>
)
})
}
'React' 카테고리의 다른 글
React - 실시간 동작을 처리하기 위해 사용되는 라이브러리 및 도구 (0) | 2024.07.06 |
---|---|
React - 프로젝트 셋팅 (PWA, Tailwind, Router, Axios, Redux-Toolkit) (0) | 2024.02.20 |
React Part 1 - Step 4 (0) | 2023.06.25 |
React Part 1 - Step 3 (0) | 2023.06.25 |
React Part 1 - Step 1 (0) | 2023.06.25 |