트위터 비스무리한 앱 프로젝트 (Twitter clone coding)
결국 리액트는 UI(매우 인터랙티브한)를 만들어주는 js 라이브러리고, 리액트를 사용하면 유지보수하기 간편해진다.
기존 자바스크립트만 써서 웹을 만드는것과는 다르다.
기존 : 명령형 방식 (일일히 하나하나 입력해서 에러 가능성이 높아지고 가독성이 떨어짐 즉 유지보수 어려움)
리액트 사용 : 선언형 방식이고, 위의 명령형 방식의 단점을 극복함.
리액트를 개발하면은 하나의 js 파일에 js와 html코드를 동시에 사용해서 작업한다. 그런데 웹브라우저는 이 스크립트를 읽을 수 없다. 그래서 우리에겐 이 스크립트를 변환해서 웹 브라우저가 이해할 수 있도록 변환시켜주는 프로젝트가 필요하다. 몇가지가 있지만 대표적으로는 Create-React-App이 있다. 이 도구를 이용하면 리액트 앱을 만들 수 있다.
Node.js를 설치해야 한다. 왜냐하면 2.1 에서 Create-React-App와 빌트인 도구들이 Node.js를 사용하기 떄문에.
그래서 이제 node가 다 설치가 되면 npx-create-react-app [프로젝트이름]
을 써서 프로젝트를 만들어준 후 vscode에서 파일을 연다.
✍ 설치된 react project
이후
터미널에서 npm start를 입력해주면 브라우저가 열리고 리액트 아이콘이 돌아가는 기본 데모웹이 실행된다.
vite
를 이용하고 싶다면 node 창에서npm create vite
를 입력한 후 폴더 이름을 생성해주고 React를 선택해주면 프로젝트가 생성된다.npm install
로 패키지를 실행한다.npm run dev
을 터미널에 입력해주면 아래처럼 성공한다.jsx
라고 함.처음에 가장 처음 실행되는 파일은 위에 보이는것처럼 main.jsx파일임. 그리고 가장 처음에 import 되는 파일들이 여러개 보이는데 react, react-dom 같은 라이브러리임. 그리고 그것들은 package.json
에 설치가 되어있음. package.json
은 Node.js가 이용함.
즉, Node.js 애플리케이션 자체를 만드는 프로젝트가 아닌 프로젝트에서 패키지 관리를 위해 이용한다. 다시말해서 package.json
에 어떤 외부파일에 의존할건지 결정된다. 리액트 라이브러리는 react
react-dom
두개가 전부다.
그리고 import를 하고나서 ReactDOM으로 시작함. 해석해보자면 root
라는 id를 갖고 있는 html 파일의 요소들을 render하라는 이야기. 무엇을? 아래의 코드들을.
<React.StrictMode>
<App />
</React.StrictMode>
그러면 <App />
를 살펴보면 위에서 import한 것들중 하나고 그 파일을 살펴보면 또 다른 jsx 스크립트이다.
사진에서 보는 것처럼 함수 하나만 있는데 이건 h1 하나만 return 함.
그리고 이런 함수를 리액트 컴포넌트라고 하고
리액트는 이 컴포넌트가 전부이다. 리액트 앱을 빌드하는건 결국 컴포넌트를 빌드하는 것이고, 결국 컴포넌트는 JSX 코드를 반환하는 함수이다.(보통)
그리고 리액트 pj에서는 최상위 컴포넌트 하나를 둔다. main.jsx
에서 출력되는 컴포넌트를 하나만 둔다는 것임. 여기선 바로 이 App 컴포넌트
가 그 역할을 한다. 즉 최상위 컴포넌트는 App 컴포넌트
임 이 안에 이 컴포넌트가 이용하는 다른 컴포넌트들을 넣어두는 구조. 그래서 새롭게 만드는 컴포넌트는 바로 이 App 컴포넌트의JSX 코드에 넣어야 함.
import Post from "./components/Post";
사용자가 만든 커스텀 컴포넌트들은 jsx 코드를 반환한다고 했다. 그리고 우리의 목적은 수십 수백가지의 컴포넌트들을 만들것임. 즉 재사용가능한 컴포넌트로 최대한 구성하는게 좋다는 말임. 따라서 하드코딩이 아닌 동적 표현식이 필요함. 그리고 이 중괄호 안에는 어떤 자바스립트 표현식도 넣을 수 있음.
이렇게 <main>
html요소로 감싸면 된다(소문자로 시작해서 html 요소)
import Post from "./components/Post";
function App() {
return (
<main>
<Post />
<Post />
<Post />
<Post />
</main>
);
}
export default App;
그리고 이렇게 컴포넌트가 여러개 있을떄, 인스턴스가 여러개 있다고도 표현한다.
같은 컴포넌트를 여러번 사용할떄에는 상위 컴포넌트로 감싸줘야한다
<>, </>
나는 지금 트위터 비슷한 앱을 만들고 있는데 거기에선 DB에서 데이터를 받아와서 UI에 그려줄 것이다. Post 컴포넌트에서 처럼 names가 매번 하드코딩 된 채로 넘어오지 않을것이다. 그래서 우리는 아래 코드를 좀 더 동적으로 만들 필요가 있다. Props를 이용해서 매번 다른 값을 전달해볼 것이다.
✍ Post.jsx
const names = ["Maximiaan", "Manuel"];
function Post() {
const chosenName = Math.random() > 0.5 ? names[0] : names[1];
return (
<div>
<p>{chosenName}</p>
<p>React.js Demo Appp start</p>
</div>
);
}
export default Post;
props를 조금 더 설명하자면 리액트 개발팀이 만든 것인데, 이것은 항상 객체이고, 안에 여러개의 속성을 담아 함수의 인자로 전달 할 수 있다.
아래에선 Post에 author
와 body
를 전달해보겠다.
✍ App.jsx
import Post from "./components/Post";
function App() {
return (
<main>
<Post author ="Maximiaan" body="the first props exercise!"/>
<Post author ="Manel" body="the second props pratice!"/>
</main>
);
}
export default App;
그리고 전달한 객체 props안에 두개의 속성이 들어있을 것이다. (author, body)
✍ Post.jsx
function Post(props) {
return (
<div>
<p>{props.author}</p>
<p>{props.body}</p>
</div>
);
}
export default Post;
우리가 만든 커스텀 속성들 이름이 이 객체 안에 각각의 속성으로 들어가게 된다는 점이다. 속성 이름을 키로 해서 값에 접근하는 것임! 여기선 커스텀 컴포넌트의 author랑 body 속성을 props.author
와 props.body
로 이용 가능하다. 리액트가 알아서 이 컴포넌트에 넣어준 객체를 이용하는 것임. 이런식으로도 컴포넌트를 재 사용할 수 있다.
.post {background-color:blue}
이렇게 작성하면 됨.✍ Post.jsx (모듈 적용전)
function Post(props) {
return (
<div className="post">
);
}
서러
그런데 프로젝트가 커질 수록 전역으로 생성한 클래스 이름이 충돌할 확률이 높아진다. 그래서 우리는 범위를 제한하는게 필요하다. 쉽게 말해서 Post 컴포넌트에만 적용할 css가 필요하다. 그래서 module
이라는 걸 이용 할 것이다.
module : Create React App이나 Vite으로 생성한 프로젝트에서 제공해주는 기술인데, CSS 파일에 정의하고 HTML이나 JSX에서 활용하는 클래스 이름이 알아서 자동으로 고유한 클래스 이름으로 변환되어 이름 충돌이 발생하지 않게 해준다.
✍ module 사용 규칙
- 보통은 관련 jsx 파일 옆에 위치함.
- 이름은 Post.module.css 로 준다. (확장자는 css, 파일 이름은 jsx파일뒤에 module을 붙임)
- css파일을 적용할 jsx 파일에 import 해준다.
ex)import classes from "./Post.module.css";
(상대경로)- jsx 파일에는 import한 css.module을 사용해준다.
ex)<div className={classes.post}>
아래 module.css 파일을 살펴보면 일반css 스크립트와 동일하다.
✍ Post.module.css
.post {
margin: 1rem 0;
padding: 1rem;
background-color: #9c7eee;
border-radius: 8px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
animation: animate-in 1s ease-out forwards;
}
.author {
font-size: 0.8rem;
font-weight: bold;
color: #543280;
margin: 0;
text-transform: uppercase;
}
.text {
white-space: pre-wrap;
font-size: 1.25rem;
margin: 0.25rem 0 0 0;
color: #593884;
font-style: italic;
}
그리고 이 CSS파일을 classes로 import했는데 이것은 객체이고, 이 객체의 css에 정의해둔 클래스는 그 객체의 프로퍼티 이름이 된다.
module.css
를 적용해보면은 {classes.post}
를 사용해 적용이 된걸 볼 수 있다.
✍ Post.jsx (모듈 적용 후)
import classes from "./Post.module.css";
function Post(props) {
return (
<div className={classes.post}>
<p className={classes.author}>{props.author}</p>
<p className={classes.text}>{props.body}</p>
</div>
);
}
export default Post;
JSX코드에서는 일반적인 html,css과는 다르게 속성값이 적용될떄가 있다.
className : 일반적으로 css의 속성값을 적용할때 html 파일에<div class="menu-category">
라고 씀.
htmlFor: HTML에서와 같이 "for" 속성 대신 "htmlFor" 속성을 사용하여 label과 input을 연결함
<div class="awesome" style="border: 1px solid red">
<label for="name">Enter your name: </label>
<input type="text" id="name" />
</div>
<p>Enter your HTML here</p>
위 html코드를 jsx로 변환하면
<>
<div className="awesome" style={{ border: "1px solid red" }}>
<label htmlFor="name">Enter your name: </label>
<input type="text" id="name" />
</div>
<p>Enter your HTML here</p>
</>
React에서는 "htmlFor"을 사용하여 해당 input과 연결할 label의 ID를 지정할 수 있고, 이렇게 하면 사용자가 label을 클릭했을 때, 연결된 input에 포커스가 이동한다. 결과적으로 "htmlFor"은 label과 input을 연결하는 속성이며, "for" 대신 사용되는 속성명