Myalog/Study
[유데미x스나이퍼팩토리] 프로젝트 캠프 : React 2기 - 사전직무교육 Day 2
myalog
2024. 8. 20. 22:04
사전직무교육 Day2
오늘은 프로젝트 캠프 React 2기 둘째날이다.
어제에 이어 타입스크립트에 대한 내용과 리액트 기초, 패키지 매니저, 빌드 도구, JSX, 컴포넌트 스타일링 등에 대해 학습했다.
타입스크립트, 특히 제네릭에 익숙하지 않은 상태여서 더 집중해서 들었다.
이번 수업내용은 이론 위주였어서 내용이 꽤 길다..! 스크롤주의!!
🌀 타입 스크립트
인터페이스
- 인터페이스는 나만의 타입 룰을 지정하는 것
- 객체를 생성하듯이 생성
- 똑같은 식별자 이름으로 된 식별자에 자동으로 병합 됨
- 대규모 애플리케이션 프로젝트를 진행할 때 타입의 이름을 중복되게 정의해서 예기치 못한 버그가 발생할 수 있으므로 주의하기
- 인덱스 시그니처
- key 타입만 알 뿐, 어떤 값이 있는지까지 명확하게 확인할 수 없음
- ex) car.name 에 접근이 불가능함
- 번거롭지만 일반적인 방법으로 모든 객체의 key에 타입을 지정해주는 것이 가장 명확하고 좋음
- key 타입만 알 뿐, 어떤 값이 있는지까지 명확하게 확인할 수 없음
interface Car{
[key: string]: string | number;
}
const car: Car = {
name: "Sonata",
color: "White",
...
}
- 상속
- 의도적으로 병합시킨 것
- 큰 인터페이스 틀에서 상세하게 잘개 쪼개고 싶을 때 사용
interface User {
name: string;
age?: number; // ? 는 옵셔널 프로퍼티 -> 필수값이 아니게 설정
}
interface Job extends User {
job: string
}
const user1Job: Job = {
name: "Mia",
job: "FE developer",
}
- readonly 키워드
- 값이 바뀌지 못하게 고정하여 인터페이스를 설정할 수 있음
interface User {
readonly name: string;
readonly age: number;
}
타입 추론
- 할당된 값을 보고 타입을 자동으로 추론해주는 특징이 있음
- 타입추론을 너무 믿으면 안정성이 떨어지는 단점이 있으므로 상황에 맞게 적절히 사용하기!
타입 별칭 (type alias)
- type 키워드를 사용하여 나만의 타입을 정의
- 변수에 할당하듯이 객체 생성
- 인터페이스와 개념이 비슷함
- IDE 기능으로, 요소에 마우스를 올려 확인할 때, 정의된 타입의 구성요소들을 확인할 수 있음
- 하지만 인터페이스는 정의된 구성 요소들을 확인하러 찾아가야함!
- 인덱스 시그니처 사용이 가능함
- 자동 병합이 되지 않음
- 상속이 되지 않음
- 인터섹션 오퍼레이터 타입을 통한 병합, 상속을 해야함
type User = {
name: string;
age: number;
}
const use: User = {
name: "Mia",
age: 20
}
type Job = {
job: string;
}
type UserAndJob = User & Job // 확장(extends) + 병합(merge)
Enum (이넘, 열거형 타입)
- 인터페이스와 비슷하지만, 파스칼 케이스로 명칭을 지어줘야함
- enum 키워드를 통해 생성
enum Role {
Admin,
User,
Guest
}
interface User {
name: string;
role: Role;
}
const user: User = {
name: "Mia",
role: Role.User,
}
- 숫자형 열거
- 자동으로 숫자 값이 할당됨, 기본 0부터 시작
- 선언된 값을 기준으로 값이 1씩 증가되어 열거됨
- 선언된 값이 없을 경우 순서대로 0부터 1씩 증가되어 열거됨
enum Role {
Admin, // 0
User, // 1
Guest // 2
}
enum Role {
Admin, // 0
User = 50, // 50
Guest // 51
}
- 문자형 열거
- 명확하게 문자열 값을 할당하고, 열거하여 값을 예측하기 쉬움
enum Role {
Admin = "admin",
User = "user",
Guest = "guest",
}
- 유니온 타입이 아닌 enum 타입을 쓰는 이유
- 재사용성이 높아짐으로 상황에 따라 맞게 사용하기!
제네릭
- 타입을 미리 지정하지 않고 사용하는 시점에 타입을 정의해서 쓰는 것
- 함수에서 제일 많이 사용됨
- 식별자 뒤에 꺽쇠(<>) 생성
- 소괄호 앞에 꺽쇠(<>) 안에 임의의 문자 삽입하여 생성
- ex) <T>
- 꺽쇠 안에 있는 문자의 타입을 받아 함수를 사용할 때 지정한 타입으로 치환됨
function getSize<T>(arr: T[]){
return arr.length;
}
getSize<number>([1,2,3]);
getSize<string>(["a", "b", "c"]);
function getReturnValue<T>(arr: T[]): T[]{
return arr;
}
getReturnValue<number>([1,2,3]);
getReturnValue<string>(["a","b","c"]);
- 타입 별칭, 인터페이스에서 사용 가능
// 기본 타입 별칭
type UserResponse = {
data: {id: number; name: string};
status: number;
}
type TodoResponse = {
data: {id: number; text: string; completed: boolean};
status: number;
}
const userResponse:UserResponse = {
data: {id: 1, name: "Mia"},
status: 200,
}
const todoResponse:TodoResponse = {
data: {id: 1, text: "리액트 공부하기", completed: false},
status: 200,
}
// 제네릭 타입 별칭
type TApiResponse<T> = {
data: T;
status: number;
}
const userResponse:TApiResponse<{id: number; name: string}> = {
data: {id: 1, name: "Mia"},
status: 200,
}
const todoResponse:TApiResponse<{id: number; name: string; completed: boolean;}> = {
data: {id: 1, text: "리액트 공부하기", completed: false},
status: 200,
}
function filterArr<T>(arr: T[], condition: (item: T) => boolean): T[]{
retrun arr.filter((el)=>condition(el))
}
const numberArr = [1,2,3,4,5]
const isEven = (num: number) => num % 2 === 0;
const evenNumbers = filterArr(numberArr, isEven);
console.log(evenNumbers)
any 타입
- 모든 타입을 허용하는 타입 정의
- any 타입을 사용하면 타입스크립트를 쓰지 않는 것과 동일하기 때문에 남용하지 않도록 해야함
- 하지만 타입스크립트 기반의 리액트 프로젝트를 하다보면 때로는 유용하게 사용할 수도 있음
- ex) Context, useState의 set 함수 등 타입을 모르거나 탕비 정의하기 애매할 경우
타입 가드
- 타입을 정의할 때, 올바르게 타입을 추론할 수 있도록 가이드를 주는 것
const add = (n1: number, n2?: number) => {
if(typeof n2 !== "undefined") return n1 + n2;
else return n1
}
const getReturnValue = (val: number | string) => {
if(typeof val === "number") return val.toFixed(1);
else return val;
}
null 보장 연산자
- ! 느낌표로 작성
- 이 값은 절대로 null 이 아니라고 명시하는 것
- 빌드 타임에서 오류가 나지 않더라도 런타임에서 에러가 날 수 있음
📦 Package (NPM vs NPX vs YARN)
- 하나의 프로젝트는 하나의 관리자만 사용할 수 있음
- 패키지? 라이브러리?
- 패키지는 node.js 관점에서 불리는 말
- 라이브러리는 javaScript 관점에서 불리는 말
기능 / 특징 | NPM | NPX | YARN |
기본 제공 | node.js 기본 패키지 매니저 | NPM 설치시 포함 (버전 5.2.0 이상) | 별도 설치가 필요함 |
패키지 설치 속도 | 보통 | - | 빠름 |
의존성 고정 파일 | pakage-lock.json | - | yarn.lock |
병렬 설치 지원 | X | X | O |
오프라인 모드 | X | X | O |
패키지 실행 | npm run <script name> | npx <pakage name> | yarn run <script name> |
사용 편의성 | 보통 | 간편함 | 사용 편의성이 높음 |
- 위 패키지 매니저 외에도 PNPM, BURN 등의 최신 패키지 매니저도 존재함
- 패키지 버전 읽는 방법
Major
- 주요 릴리즈
- 패키지에서 엄청난 변화가 있을 경우에 해당 위치의 숫자를 증가시킴
- 주로 이전 버전과 호환성을 깨트릴 정도의 중요한 패치일 경우 변경됨
Minor
- 새로운 기능
- 패키지에서 새로운 기능이 추가되었을 경우에 해당 위치의 숫자를 증가시킴
- 이전 버전과의 호환성은 유지됨
Patch
- 버그 수정
- 기존에 포함되어있던 기능에 대한 버그를 수정했을 경우 해당 위치의 숫자를 증가시킴
- 이전 버전과의 호환성은 유지됨
Optional
- 특정 버전 뒤에 문자열로 된 의미를 부여하고 싶을 때 사용됨
💙 React
리액트가 탄생하게 된 배경
- 주소창에 도메인을 입력할 때 브라우저에서는 중요 렌더링 경로(Critical Rendering Path)가 발생함
- 중요 렌더링 경로(Critical Rendering Path)란 브라우저가 HTML, CSS, javaScrip를 화면에 그려내는 일련의 단계를 말함
- DOM 요소를 layout, paint 작업을 거쳐 화면에 보여지게 되는데, layout과 paint 작업은 코스트가 높은 작업임
- reflow와 repaint를 최대한 적게 발생시키는게 개발자들의 과제였음
- 이를 해결하기 위해 react, vue와 같은 프레임워크/라이브러리가 탄생하게 되었음
- 실제로 가상 DOM보다 DOM이 더 빠른 경우도 있기 때문에, 가상 DOM을 활용하여 성능을 최적화한다는 것은 모순이 되는 경우가 있기 때문에 DX 향상을 위해 리액트를 활용한다는 말이 더 일리가 있음
가상 DOM (Virtual DOM)
- Orginal DOM 을 복사한 것
- React의 가상 DOM은 두 개
- 한번에 발생할 수 있는 모든 변화를 가상 DOM에 저장
- 업데이트 된 가상 DOM과 또 다른 가상 DOM을 비교
- 디핑 (diffing)
- 가상 DOM을 서로 비교해서 최종적으로 달라진 부분을 찾아내는 과정
- 재조정 (Reconciliation)
- DOM이 변경된 것을 판단 후 Original DOM을 업데이트
컴포넌트 작성 방법
- createElement()
- 리액트 출시 초기에는 JSX 문법을 해석할 수 있는 바벨과 같은 변환 컴파일러의 존재가 없었기 때문에 메서드를 통해 컴포넌트를 작성함
const element = createElement(type, props, ...children);
const divEl = createElement("div", {id:"name"}, "Hello"); // <div id="name">Hello</div>
import { createElement } from 'react';
function Greeting() {
// <div>
// <h1 class="greeting">Hello, React!</h1>
// </div>
return createElement(
"div",
null,
createElement("h1", { className: "greeting" }, "Hello, React!"),
createElement("h2", { className: "wow" }, "WOW!"), // 4번째 인자부터는 형제로 요소를 만들 수 있음
);
}
- JSX 문법을 사용해서 컴포넌트를 작성
- JSX 문법은 반드시 하나의 루트 태그로 구성되어야 함
- ex) Fragment 태그 등
- 여러 줄의 코드를 리턴할 경우 소괄호 사용
- self-closing 태그의 경우 닫는 태그로 사용
- 표현식은 중괄호 사용
- class 속성은 className 으로 사용
- 함수형 컴포넌트의 이름은 항상 대문자로 시작
- JSX 문법은 반드시 하나의 루트 태그로 구성되어야 함
컴포넌트 CSS 스타일링
- 인라인 스타일
- 태그에 직접 style 속성을 사용하여 적용
- 속성에 대시(-) 기호가 있을 경우 카멜 케이스로 변경하여 작성
const App = () => {
return (
<div>
<h1
style={{
fontSize: "24px",
color: "#eee",
textDecoration: "line-through",
}}
>
Hello World!
</h1>
</div>
);
};
export default App;
- 외부 스타일 / 전역 스타일
- 외부 CSS 파일을 만들어 적용
- import 로 불러오기
- import 로 불러오게되면 모든 컴포넌트에 스타일이 적용됨
- 따라서 외부 스타일링 또는 전역 스타일링이라고도 함
- 유지보수 차원에서 main.tsx 파일에 전역적으로 작성된 스타일 코드를 import 해오는게 좋음
- import 로 불러오기
- 외부 CSS 파일을 만들어 적용
- CSS Modules
- 리액트 팀이 가장 권장하는 스타일링 방법
- 특정 컴포넌트에만 스타일을 적용하고 싶을 때 사용
- 컴포넌트명.module.css 로 파일명 생성
- import 후 해당 className을 적용하여 사용
- class를 로컬라이징하여 스타일 적용
- CSS 파일 내 태그 식별자를 사용할 수 없음
- ex) h1, p, div
- 하지만 class로 시작하는 선택자의 자식 선택자가 태그 식별자일 경우에는 사용 가능
- 자식 선택자에 동일한 태그 식별자가 있을 경우 해당 되는 식별자의 모든 스타일이 바뀔 수 있으니 주의!!
- CSS 파일 내 태그 식별자를 사용할 수 없음
.container h1{
background-color: yellow;
}
- 서드 파티 라이브러리 ( = 다른 사람이 만든 라이브러리 )
- classnames
- 웹팩 기반 프로젝트 일 경우 require로 가져오기
- vite 기반 프로젝트 일 경우 import로 가져오기
- className이 2개 이상일 때 가독성 측면에서 유리하여 많이 사용됨
- CSS Modules와 함께 사용하려면 import 해올 때 경로를 "classnames/bind" 로 가져옴
- classnames
const classNames = require('classnames');
classNames('foo', 'bar'); // => 'foo bar'
classNames('foo', 'bar'); // => 'foo bar'
classNames('foo', { bar: true }); // => 'foo bar'
classNames({ 'foo-bar': true }); // => 'foo-bar'
classNames({ 'foo-bar': false }); // => ''
classNames({ foo: true }, { bar: true }); // => 'foo bar'
classNames({ foo: true, bar: true }); // => 'foo bar'
// lots of arguments of various types
classNames('foo', { bar: true, duck: false }, 'baz', { quux: true }); // => 'foo bar baz quux'
// other falsy values are just ignored
classNames(null, false, 'bar', undefined, 0, 1, { baz: null }, ''); // => 'bar 1'
const arr = ['b', { c: true, d: false }];
classNames('a', arr); // => 'a b c'
오늘은 이론 위주로 배운 내용이 많아 이해하고, 정리하느라 수업시간이 빠르게 흘렀다.
강사님이 실무에서 실제로 많이 쓰이고, 꿀팁으로 활용되는 부분들을 중간에 알려주셔서 실무에선 어떻게 활용되는지 더 이해하기 수월했다.
내일도 화이팅!!! 🥹
본 후기는 본 후기는 [유데미x스나이퍼팩토리] 프로젝트 캠프 : React 2기 과정(B-log) 리뷰로 작성 되었습니다.