와이파이가 없어서 한글에 정리해놓은건데 잃어버릴 수 있으니까 여기에다가 올려놔야겠다 ㅎㅎ
React JS
#1. 강의소개
- create-react-app을 이용해 functional component 작성법과 JSX를 배움
- React Hook(useState, useEffect)도 함께 다룸
CRUD 구현을 위해 JSON-Server를 띄우고 Rest API를 통신하며 단어들을 추가(POST), 수정(PUT), 삭제(DELETE)하는 작업
=> 강의를 듣기 전 node.js(프레임워크),VSCode(텍스트 에디터) 설치, html과 javaScript 지식
#2. 설치
적당한 폴더를 만들어 cmd창 띄우기
npx create-react-app 프로젝트명
npx: npm에 올라가 있는 패키지를 바로 실행해서 설치시켜주는 도구
VSCode에서 폴더 열기
터미널 열어서 해당 프로젝트 이름으로 이동(cd 프로젝트명) 후 npm start
node_modules는 이 프로젝트를 실행할 때 사용되는 dependencies 모듈들이 모두 모여있음(package.json에서 볼 수 있음)-> 폴더를 지우면 프로젝트를 띄울 수 없음, 폴더를 지우더라도 package.json파일만 수정되지 않았다면 npm install 해주면 그대로 다시 설치 가능
(크기가 굉장히 크고 파일도 많기 때문에 node_modules 폴더는 git에 올리지 않음)
public 폴더 내에 index.html 파일에 있는 <div id = “root”></div>로 리액트 코드가 실행돼서 만들어진 DOM이 구현되게 된다.
index.js 파일을 보면 App.js 파일을 불러오는 부분을 볼 수 있음(import App from ‘.App’;)
id root에 App을 렌더링 시켜준다(
ReactDOM.render(
<React.StrictMode>
<App/>
<React.StrictMode>,
document.getElementById(‘root’)
);).-> index.html에 있던 root
App.js에서 구현하면 됨(여기서 뭔가를 작성하거나 수정하면 브라우저에 바로 반영(Hot Module Replacement(HMR)).
컨트롤 C를 누르면 프로젝트가 내려가고, npm start로 다시 띄울 수 있음
(명령어는 package.json 파일에 명시)
start는 개발 모드로 프로그램 실행
build는 실제 배포 모드로 만들어줌
test는 테스트
eject는 내부 설정 파일을 꺼내는 역할(웹페이지나 바벨(?) 설정을 변경하고 싶을 때 사용)
App.js를 보면 class가 자바스크립트의 예약어라 className
JSX는 자바스크립트 내부에 HTML처럼 작성하는 것
#3. 컴포넌트, JSX
리액트로 만든 페이지는 컴포넌트들로 구성되어 있다. 페이지 단위로 HTML을 작성하는 것이 아니라 각 부분을 컴포넌트로 만들어 조립해서 사용(비슷한 부분들은 코드를 재사용할 수 있고, 유지보수도 쉬어짐)
function App(){} -> 함수로 만들어진 앱 컴포넌트(함수형 컴포넌트)
-> 이 함수가 리턴하는 것은 JSX(JavaScript XML)
이거를 index.js에서 import해서 사용
모든 컴포넌트는 대문자로 시작해야 함
function App() {
const name = "Tom"
return (
<div className="App">
<h1
style={{
color:"red",
backgroundColor:"green"
}}>
Hello, {name}.
<p>{2 + 3}</p>
</h1>
</div>
);
}
- backgroundColor 같은 경우에는 –로 연결해주는 것이 아닌 대문자로 구분해주는 카멜 케이스(Camel case)로 작성
- {} 내부에서 변수명 사용
- 문자열이나 숫자는 잘 나오지만 boolean 타입이나 객체는 표현하지 못함
function App() {
const name ="Tom";
const user = {
name:"Jane",
};
return (
<div className="App">
<h1
style={{
color:"red",
backgroundColor:"green",
}}>
Hello, {name}.
<p>{user}</p>
</h1>
</div>
);
}
-> 이런 식으로 user 객체를 만들고 실행하면 오류
function App() {
const name = "Tom";
const naver = {
name: "네이버",
url: "https://naver.com",
};
return (
<div className="App">
<h1
style={{
color:"red",
backgroundColor:"green",
}}>
Hello, {name}.
<p>{2 + 3}</p>
</h1>
<a href={naver.url}>{naver.name}</a>
</div>
);
}
-> 객체를 만들어 링크를 만들었음
#4. 첫 컴포넌트 만들기
컴포넌트라는 폴더를 만든다.
js파일과 함수 만들고 export 해주면 컴포넌트를 만들 수 있음(항상 대문자로 시작)
const Hello = function(){
return <p>Hello</p>
}
export default Hello;
또는
const Hello = () => {
return <p>Hello</p>
}
export default Hello;
또는
export default function Hello(){
return <p>Hello</p>;
}
App.js에 import
import Hello from './component/Hello'; -> .js는 빼고 적어도 됨
function App() {
return <div className="App">
<Hello></Hello>
</div>
}
-> 이런 식으로 쓰는데 태그 안에 아무것도 적는 것이 없으면
function App() {
return <div className="App">
<Hello />
</div>
}
->셀프 크로즈해주는 것이 좋음
import World from "./World";
export default function Hello(){
return (
<div>
<h1>Hello</h1>
<World />
</div>
);
}
-> div 태그가 없으면 오류가 남(JSX는 하나의 태그만 만들 수 있기 때문)
한 번 만들어놓은 컴포넌트는 계속해서 재사용할 수 있다!!
#5. CSS 작성법(module css)
create-react-app으로 만든 프로젝트라면 별도의 다른 패키지 설치 없이 사용할 수 있는 방법 3가지
인라인 스타일(css 파일을 따로 작성할 필요 없이 html 태그에 바로 작성)
스타일 안은 {}를 한 번 더 작성해서 객체로 만들어야 함
객체이기 때문에 세미콜론(;)이 아니라 쉼표(,)
데쉬(-)가 아닌 카멜 케이스로 작성(대문자로 구분)
속성값은 문자열이면 따옴표로 감싸줘야 함
숫자는 그냥 숫자를 써도 되지만 픽셀 같은 것은 따옴표로 감싸줘야 함
import World from "./World";
export default function Hello(){
return (
<div>
<h1 style={{
color : '#f00',
borderRight : '12px solid #000',
marginBottom : '50px',
opacity : 1,
}}
>
Hello
</h1>
<World />
<World />
</div>
);
}
-> 특별한 이유가 없으면 인라인으로 작성하지 않음
2. css 파일 활용(class를 추가하며 사용할 수 있음)
index.css는 전체 프로젝트에 영향을 미침
App.css는 App 컴포넌트에 한정된 내용들이 들어가 있음
-> css 파일들이 각 컴포넌트에 종속되는 것이 아닌 헤드 부분에 다 작성되기 때문에 전 페이지(맨 처음 적용한 것)에 다 영향을 미치게 됨
3. 각 컴포넌트에 특화된 css를 작성(css module 활용)
파일명은 보통 컴포넌트의 이름에 맞춰적어줌(Hello.module.css)
Hello.js에 import 해줄 때는 styles로 -> import styles from "./Hello.module.css";
className을 적어줄 때는 {styles.~~}로 적기-> <div className={styles.box}>Hello</div>
-> 동일한 내용을 작성하더라도 서로 중복될 일이 없음(css 파일이 커지면 가장 고민되는 것이 네이밍이랑 상속, 오버라이딩에 대한 부분인데, 이 방식으로 말끔하게 해결할 수 있음)
글로벌 단위가 아닌 컴포넌트 단위로 관리되는 것이 장점
그냥 index.css에 작성한다 함
VSCode 단축키 정리
단축키 | 명령 | 설명 |
ctrl+d | 다음 선택 | 블록 선택 후 입력 시 같은 다음 블록이 중복 선택 |
ctrl+x | 행 삭제(빈 선택) | 커서의 1행 삭제 |
ctrl+s | 저장 | 현재 파일 저장 |
ctrl+alt+s | 모두 저장 | 열린 파일 모두 저장 |
ctrl+\ | 편집기 나누기 | 편집창 분할 |
ctrl+숫자 | 편집창 이동 | 입력된 숫자의 편집창으로 이동 |
ctrl+k ctrl+왼쪽 | 왼쪽 편집창으로 이동 | 왼쪽 편집창으로 이동(ctrl+k누른 후, ctrl+왼쪽 방향 키 누름) |
ctrl+k ctrl+오른쪽 | 오른쪽 편집창으로 이동 | 오른쪽 편집창으로 이동(ctrl+k누른 후, ctrl+오른쪽 방향 키 누름) |
shift+alt+Down | 위에 행 복사 추가 | 위에 행 복사 추가 |
shift+alt+Up | 아래 행 복사 추가 | 아래 행 복사 추가 |
ctrl+shift+Enter | 위에 행 삽입 | 커서 위에 행 삽입 |
ctrl+g | 행 이동 | 입력된 라인 넘버의 행으로 이동 |
F12 | 정의로 이동 | 정의로 이동 |
shift+F12 | 참조 표시 | 해당 function을 사용하고 있는 부분 표시 |
shift+Alt+F/전체 선택 후 ctrl+K+F: 자동 정렬 |
#6. 이벤트 처리(Handling Events)
- 이벤트를 거는 첫 번째 방법
미리 함수를 만들어놓고 전달
button의 속성으로 onClick은 카멜 케이스로 작성해주고 전달하고자 하는 내용은 문자열이 아니기 때문에 {}
export default function Hello(){
function showName(){
console.log("Mike");
}
return (
<div>
<h1>Hello</h1>
<button onClick={showName}>이름 보이기</button>
<button>나이 보이기</button>
</div>
);
}
-> 만약 showName()이라고 써주면 반환하는 값을 출력해주기 때문에 (지금은 반환되는 값이 없으니)undefined가 들어가게 됨
두 번째 방법
onClick 내부에 직접 함수 구현
export default function Hello(){
function showName(){
console.log("Mike");
}
return (
<div>
<h1>Hello</h1>
<button onClick={showName}>이름 보이기</button>
<button onClick={()=>{
console.log(30);
}}
>
나이 보이기
</button>
</div>
);
}
------------------------
export default function Hello(){
function showName(){
console.log("Mike");
}
function showAge(age){
console.log(age);
}
return (
<div>
<h1>Hello</h1>
<button onClick={showName}>이름 보이기</button>
<button onClick={()=>{
showAge(10);
}}
>
나이 보이기
</button>
</div>
);
}
-> (매개변수를 전달하기 편함)
----------------------------------------
export default function Hello(){
function showName(){
console.log("Mike");
}
function showAge(age){
console.log(age);
}
function showText(e){
console.log(e.target.value);
}
return (
<div>
<h1>Hello</h1>
<button onClick={showName}>이름 보이기</button>
<button onClick={()=>{
showAge(10);
}}
>
나이 보이기
</button>
<input type="text" onChange={showText}/>
</div>
);
}
바뀔 때마다 찍혀야 되니까 onChange를 써줌
showText는 e(이벤트 객체)로 받음
taget은 input 태그가 됨
value는 input의 값이니까 작성한 값
== 위의 코드와 동일함
export default function Hello(){
function showName(){
console.log("Mike");
}
function showAge(age){
console.log(age);
}
return (
<div>
<h1>Hello</h1>
<button onClick={showName}>이름 보이기</button>
<button onClick={()=>{
showAge(10);
}}
>
나이 보이기
</button>
<input type="text" onChange={(e)=>{
console.log(e.target.value)
}}/>
</div>
);
}
==위의 코드와 동일함
export default function Hello(){
function showName(){
console.log("Mike");
}
function showAge(age){
console.log(age);
}
function showText(txt){
console.log(txt);
}
return (
<div>
<h1>Hello</h1>
<button onClick={showName}>이름 보이기</button>
<button onClick={()=>{
showAge(10);
}}
>
나이 보이기
</button>
<input
type="text"
onChange={e=>{
const txt = e.target.value;
showText(txt);
}}/>
</div>
);
}
#7. state, useState (상태값)
state – 컴포넌트가 갖고 있는 상태값(상태값이 변하면 리액트는 자동으로 UI를 업데이트)
export default function Hello(){
let name = "Mike"
function changeName(){
name = name === "Mike" ? "Jane" : "Mike";
console.log(name);
//바닐라스크립트로 작업을 한다면 DOM 업데이트 작업을 하기 위해 다음 코드 추가
document.getElementById("name").innerText = name;
}
return (
<div>
<h1>state</h1>
<h2 id="name">{name}</h2>
<button onClick={changeName}>바꾸기</button>
</div>
);
}
-> id가 “name”인 element에 text를 name으로 바꿔주고 있음
여기서 name은 state가 아니라 변수임(이 컴포넌트가 관리하고 있는 상태값이 아님) -> 바뀌어도 리액트는 인지하지 못하고 UI를 업데이트하지 못한다.
어떻게 하면 state로 만들 수 있을까?
->첫 번째 리액트인 useState를 사용한다.
리액트 16.8부터 모든 컴포넌트를 함수형으로 만들 수 있게 되었고, 리액트 Hook을 이용해 함수형 컴포넌트에서도 state와 lifecycle 관리가 가능해짐
useState(상태값 관리)
import {useState} from "react";
export default function Hello(){
//let name = "Mike";
const [name, setName] = useState('Mike');
function changeName(){
const newName = name === "Mike" ? "Jane" : "Mike";
//document.getElementById("name").innerText = name;
setName(newName);
}
return (
<div>
<h1>state</h1>
<h2 id="name">{name}</h2>
<button onClick={changeName}>바꾸기</button>
</div>
);
}
.................. 이렇게 작성해도 됨
function changeName(){
setName(name === "Mike" ? "Jane" : "Mike");
}
.................
또는
import { useState } from "react";
export default function Hello() {
//let name = "Mike";
const [name, setName] = useState('Mike');
return (
<div>
<h1>state</h1>
<h2 id="name">{name}</h2>
<button
onClick={() => {
setName(name === "Mike" ? "Jane" : "Mike");
}}
>
바꾸기
</button>
</div>
);
}
const [name, setName] = useState('Mike');//구조 분해 할당
useState는 배열을 반환하는데 첫 번째 값은 state(name..변수명이라 생각하면 됨)이고 두 번째는 이 state를 변경해주는 함수(setName이 호출돼서 name이 바뀌면 리액트는 이 컴포넌트(Hello)를 다시 렌더링 해줌)
useState()의 ()안에는 초기값을 넣어주면 됨
Hello 컴포넌트를 App에 3번 그리면 동일한 컴포넌트라도 state는 각각 관리됨(다른 state에 영향을 주지 않음)
#8. props
App.js |
import './App.css'; import Hello from './component/Hello'; function App() { return ( <div className="App"> <h3>props : properties</h3> <Hello age={10} /> <Hello age={20} /> <Hello age={30} /> </div> ); } export default App; |
Hello.js |
import { useState } from "react"; export default function Hello(props) { const [name, setName] = useState('Mike'); return ( <div> <h2 id="name"> {name}({props.age})</h2> <button onClick={() => { setName(name === "Mike" ? "Jane" : "Mike"); }} > 바꾸기 </button> </div> ); } |
props(properties(속성값)의 약자): 이 값은 컴포넌트 내부에서 변경할 수 없음(넘겨받은 그대로 사용)-> 값을 변경하고 싶으면 컴포넌트 내부에서 state를 다시 만들어야함
Hello.js |
import { useState } from "react"; export default function Hello(props) { const [name, setName] = useState('Mike'); const [age, setAge] = useState(props.age); return ( <div> <h2 id="name"> {name}({age})</h2> <button onClick={() => { setName(name === "Mike" ? "Jane" : "Mike"); setAge(age + 1); }} > 바꾸기 </button> </div> ); } |
-> 넘겨받은 값을 변경하고 있는 것이 아닌 해당 컴포넌트에서 만든 age가 바뀌는 것임
age만 나오니까 다음과 같이 바꿔줄 수 있음(자바스크립트: 구조 분해 할당) |
import { useState } from "react"; export default function Hello({ age }) { const [name, setName] = useState('Mike'); return ( <div> <h2 id="name"> {name}({age})</h2> <button onClick={() => { setName(name === "Mike" ? "Jane" : "Mike"); }} > 바꾸기 </button> </div> ); } |
프롭스로 정보를 받고 이용하는 예
import { useState } from "react"; export default function Hello({ age }) { const [name, setName] = useState('Mike'); const msg = age > 19 ? "성인 입니다." : "미성년자 입니다."; return ( <div> <h2 id="name"> {name}({age}) : {msg} </h2> <button onClick={() => { setName(name === "Mike" ? "Jane" : "Mike"); }} > 바꾸기 </button> </div> ); } |
화면에 어떤 데이터를 갱신하기 위해서 state와 props를 사용해 처리하는 것이 좋음
- 한 컴포넌트가 가지고 있는 state를 props로 넘기기
UserName.js |
export default function UserName({name}){ return <p>Hello, {name}</p> } |
Hello.js |
import { useState } from "react"; import UserName from "./UserName"; export default function Hello({ age }) { const [name, setName] = useState('Mike'); const msg = age > 19 ? "성인 입니다." : "미성년자 입니다."; return ( <div> <h2 id="name"> {name}({age}) : {msg} </h2> <UserName name={name} /> <button onClick={() => { setName(name === "Mike" ? "Jane" : "Mike"); }} > 바꾸기 </button> </div> ); } |
-> 파란부분에서 name은 Hello 컴포넌트에서는 state이지만 UserName 컴포넌트 입장에서는 props
#9. 더미 데이터 구현, map() 반복문
API가 없는 상태이기 때문에 더미 데이터를 하나 만들어서 사용
날짜와 단어를 배열로 만들고 정보들을 입력
만든 더미 데이터를 불러오기(Daylist.js)
li로 day 개수만큼 그려줘야하는데 반복문을 사용해야함 -> map(배열을 받아서 또 다른 배열을 반환->이때 반환되는 요소는 JSX로 작성)을 사용하는게 편함
key는 반복되는 요소의 고유한 값을 넣어줘야함(id)
import dummy from "../db/data.json"; export default function DayList(){ console.log(dummy); return <ul className="list_day"> {dummy.days.map(day => ( <li key={day.id}>Day {day.day}</li> ))} </ul>; } |
특정 날짜에 단어들이 나오는 페이지(Day.js)-> 단어들이 쭉 나오니까 테이블로 구현
import dummy from "../db/data.json"; export default function Day(){ //dummy.word const day = 1; const wordList = dummy.words.filter(word => word.day===day); console.log(wordList); return<> <table> <tbody> {wordList.map(word => ( <tr key={word.id}> <td>{word.eng}</td> <td>{word.kor}</td> </tr> ))} </tbody> </table> </>; } |
다음 시간은 페이지 이동, 라우팅
# 10. 라우터 구현(react-router-dom)
주소로 /를 하면 첫 페이지로 이동
'웹' 카테고리의 다른 글
[강의] 학습중... (0) | 2024.04.24 |
---|---|
[인프런] 기초 HTML (0) | 2024.03.30 |
[인프런] 웹 개발 시작해봐요 (0) | 2024.03.18 |