스나이퍼팩토리 사전직무교육 (Day 2)

짜스의 하루 ·2024년 9월 23일

타입 오퍼레이트

자바스크립트의 OR(||) 연산자와 AND(&&) 연산자와 같은 역할을 하는 타입스크립트 오퍼레이터는 유니언 타입과 인터섹션 타입이 있다.

유니언 타입 -> | , || -> OR

유니언 타입은 OR(||) 연산자와 같은 역할을 하는 타입스크립트 오퍼레이터(|)로 여러 개의 타입을 결합한 타입을 말한다. 유니언 타입은 연결된 타입 중 1개만 만족하면 된다.

const returnValue = (value : string | number | boolean ) => {
	console.log(returnValue("A");
	console.log(returnValue(1);
	console.log(returnValue(true);
}

주의해야할 점

const sum = (n1:number | string, n2 : number | string) : number | string => {
	n1 + n2
}

이렇게 하게되면 타입 오류가 나게 된다 왜?

n1 -> number , n2 -> number -> ok
n1 -> string , n2 -> string -> ok
n1 -> string , n2 -> number -> error

가 되게 되는 것이다.
그럼 어떻게 해야할까 ?

const sum = (n1:number | string, n2 : number | string) : number | string => {
	if(typeof n1 ==="number" && typeof n2 === "number") return n1 + n2;
    else if(typeof n1 === " string" && typeof n2 === "string") return n1+n2;
    
    throw new Error("ParameterNot Valid");
};

const res1 = add(10,20);
const res2 = add("A", "B");

console.log(res1, res2)

인터섹션 & AND

인터섹션 타입은 AND(&&) 연산자와 같은 역할을 하는 타입스크립트 오퍼레이터(*)를 사용하여 2개 이상의 타입을 결합한 타입을 말한다. 인터섹션 타입은 연결된 타입을 모두 만족해야 한다.

let user1 = {name : string} & {age : number} & {gender : string} = {
	name : 'seoYeon',
	age : 25,
	gender : "여자"
};

타입 추론

TypeScript는 변수에 값을 할당할 때, 명시적으로 타입을 지정하지 않더라도 해당 값에 따라 자동으로 타입을 추론하게 된다.

let num = 10
let str = "A"

let num = 10: number 타입으로 추론.
let str = "A": string 타입으로 추론.


타입 별칭

타입 별칭(type alias)은 TypeScript에서 특정 타입을 정의하고 재사용할 수 있도록 도와주는 기능을 의미한다.

--> type 키워드를 사용하여 새로운 타입 이름을 정의할 수 있으며, 이는 객체, 배열, 유니온 타입 등 다양한 형태로 나타날 수 있다.

type User = {
name : string;
age : number;
gender : 'male' | 'female';
}

let user1: User;

타입 별칭을 사용하는 이유는 ?
주요 특징

  • 가독성 향상: 타입 별칭을 사용하면 복잡한 타입을 더 이해하기 쉽게 만들 수 있다.
    예를 들어, 여러 곳에서 사용되는 객체 구조를 User라는 이름으로 정의함으로써 코드의 가독성을 높여준다.

  • 재사용성: 한 번 정의한 타입 별칭은 다른 변수에서 쉽게 재사용할 수 있다. 이를 통해 코드 중복을 줄이고 유지 보수성을 높일 수 있다.

  • 구조적 타입: 타입 별칭은 구조적 타이핑을 지원한다. 즉, 속성이 모두 일치하지 않아도 필요 속성만 있으면 해당 타입으로 간주된다.


Interface

interface의 기본 문법

interface  User {
   .....
}

상속

User 인터페이스는 name, age, gender 속성을 가지고 있고,
Developer 인터페이스는 User를 상속받아 skill 속성을 추가로 가지게 된다.

interface User {
	name : string,
    age : number,
    gender : string
}

interface Developer extends User {
	skill : string 
}

let dev1 : Developer = {
	name : 'seoYeon',
    age : 25,
    gender : 'male',
    skill : 'next.js',
 }
 

그럼 type은 병합이 안될까 ? --> 이렇게 가능하다

type Name = {
  name: string
}

type Skill = {
  skill: string
}

type User = Name & Skill; // Name과 Skill을 결합하여 User 타입 정의

let user1: User = {
  name: 'seoYeon',
  skill: 'next.js'
}

type VS interface

선언 방식:

  • interface: 클래스를 정의하듯이 객체의 구조를 선언하는 데 사용된다. 여러 번 선언할 수 있으며, 같은 이름의 인터페이스가 있으면 병합된다.
  • type: 보다 유연하게 사용할 수 있으며, 객체뿐만 아니라 원시 타입, 유니온, 튜플 등 다양한 타입을 정의할 수 있다.

확장성:

  • interface: 다른 인터페이스를 상속받을 수 있으며, 이를 통해 계층 구조를 만들기 쉽다.
  • type: 인터섹션(&)을 사용하여 여러 타입을 결합할 수 있지만, 상속 개념은 없다.

병합:

  • interface: 동일한 이름의 인터페이스가 여러 번 선언될 경우, 자동으로 병합된다.
  • type: 같은 이름의 타입을 여러 번 선언할 수 없으며, 에러가 발생한다.

어떤 것을 사용할까?
interface를 사용하는 경우:

  • 객체의 구조를 명확하게 정의하고자 할 때.
  • 클래스와의 상호작용이 있을 때 (클래스가 인터페이스를 구현할 경우).
  • 여러 개의 선언 병합이 필요한 경우.

type을 사용하는 경우:

  • 유니온 타입, 튜플 등 다양한 타입 조합을 사용하고자 할 때.
  • 좀 더 간단한 표현이 필요할 때.
  • 객체의 형태가 복잡하지 않을 때.

결론
둘 다 유용하며, 상황에 따라 선택하면 됩니다. 일반적으로 객체의 구조를 정의할 때는 interface를, 복잡한 타입 조합이나 단순한 타입 정의에는 type을 사용하는 것이 좋다.


Enum

타입스크립트의 enum은 열거형(enum)을 정의하는 데 사용되는 데이터 타입입니다. 열거형은 관련된 상수 집합을 그룹화하여 코드의 가독성과 유지보수성을 높이는 데 도움을 줍니다. 각 열거형 값은 이름과 함께 숫자 또는 문자열로 매핑됩니다.

타입스크립트에서 enum을 사용하면 열거형을 정의하고, 이를 통해 상수 값을 보다 의미 있게 사용할 수 있습니다. 열거형은 다음과 같은 방식으로 정의하고 사용할 수 있습니다.

숫자형 열거
기본적으로, 타입스크립트의 열거형은 숫자 열거형으로 정의된다.
--> 열거형의 각 멤버는 자동으로 0부터 시작하는 숫자 값을 갖는다.

enum Direction {
	Up, //0
	Down, //1
	Left, //2
	Right, //3
}

문자열 열거
문자열 열거형은 각 열거형 멤버가 문자열 값을 가지도록 정의할 수 있다.
--> 문자열 열거형은 값이 명확하고 예측 가능하므로 더 직관적이게 된다.

enum BenzColor {
	Red= "Red",
    White = "white",
    Black = "black",
}

type Benz = {
	name = string,
    speed = number,
    color = "red" | "white" | "black"
}
 
const car1 : Benz = {
 name : "s class",
 speed : 200,
 color : BenzColor.Red
}

변하지 않는 상수값을 지정한다.
그 값들에 연관관계가 있고, 정의해두면 좋을 것들을 enum에 정의해둔다고 간단하게 이해하면 된다.


함수 + 제네릭

제네릭(Generics)은 TypeScript에서 타입을 유연하게 정의할 수 있게 해주는 기능이다.
제네릭을 사용하면 특정 타입에 의존하지 않고, 다양한 타입에 대해 재사용 가능한 함수를 작성하는 것이다.

function returnValue<T>(arr : T[]) : T {
	return arr[0];
  }
  
const res1 = returnValue<number>([1,2,3]);
const res2 = returnValue(['a','b','c'])

function returnValue<T>(arr: T[]) : T에서 T는 제네릭 타입 변수이다.
이 함수는 배열을 인자로 받고, 해당 배열의 첫 번째 요소를 반환하게 된다.
--> 반환 타입은 T로, 배열의 요소 타입과 동일하다.

returnValue<number>([1, 2, 3]): number 타입을 명시적으로 지정하여, 반환되는 값의 타입이 number임을 보장하게 된다.

인터페이스에서의 타입

interface Mobile {
	name : string;
	color : string;
	option : any
 }

let mobile1 : Mobile = {
   name : "Galaxy S21",
   color : "white",
   option : "없음",
 
 }
 
let mobile2 : Mobile<{bell:string}> = {
	name : "Galaxy S21",
	color : "white",
	option : "없음",
	option : {
	bell : '3개월'
	}
}

제네릭 연습문제

문제 1)

다음 함수들을 타입스크립트로 작성하고 타입을 정의해 보세요.

  • 배열을 입력받아 배열의 요소들을 역순으로 반환하는 제네릭 함수를 작성하세요.
// reverse 함수 구현

// 사용 예제
const numArray = [1, 2, 3];
const reversedNumArray = reverse(numArray);  // [3, 2, 1]

const strArray = ["a", "b", "c"];
const reversedStrArray = reverse(strArray);  // ["c", "b", "a"]

해결
//1번

const numArray = [1, 2, 3];

const reverse = (arr: number[]) => {
    return [...arr].reverse(); 
}

const reversedNumArray = reverse(numArray);  
console.log(reversedNumArray);


// 2번

const strArray = ["a", "b", "c"];

const reverse2 = (arr: string[]) => {
    return [...arr].reverse(); 
}

const reversedStrArray = reverse2(strArray);  
console.log(reversedStrArray);

여기서 생각해 봐야할 것은 , 배열인 상태에서 .reverse()를 통해서 반대된 배열로 변경하도록 !
[...arr].reverse()를 하게 되면 정답이 된다!

그런데, 제네릭으로 하게 된다면,

const numArray = [1, 2, 3];
const reversedNumArray = reverse<number>(numArray); // [3, 2, 1]

const strArray = ["a", "b", "c"];
const reversedStrArray = reverse<string>(strArray); // ["c", "b", "a"]

이렇게 사용하면 된다!

문제 2 ) Todo List 코드 타입

아래의 코드는 Todo List에서 사용되는 코드를 자바스크립트로 작성한 코드입니다.

const todos = [];

const addTodo = (text) => {
  const todo = {
    id: todos.length + 1,
    text,
    completed: false,
  };
  todos.push(todo);
};

const removeTodo = (id) => {
  const findIndex = todos.findIndex((todo) => todo.id === id);
  todos.splice(findIndex, 1);
};

const toggleTodo = (id) => {
  const find = todos.find((todo) => todo.id === id);
  find.completed = !find.completed;
};

위 코드를 타입스크립트로 변경하세요.

해결

interface IText {
    id : number,
    completed : boolean
}

const todos: IText[] = [];


const addTodo = (text : string) => {
  const todo = {
    id: todos.length + 1,
    text,
    completed: false,
  };
  todos.push(todo);
};

const removeTodo = (id : number) => {
  const findIndex:number= todos.findIndex((todo) => todo.id === id);
  todos.splice(findIndex, 1);
};

const toggleTodo = (id : number) => {
    const find: IText | undefined = todos.find((todo) => todo.id === id);
    if(find) {
        find.completed = !find.completed;
    }
};

React

리액트를 실행시키는 방법
1 . CRA(Create React App)
2 . Vite

CRA 방법으로 프로젝트를 생성해보자

mkdir cra 를 통해서 cra의 폴더를 생성하고
cd cra cra 폴더로 이동한 후 ,
npx create-react-app . 를 실행시키면 된다.
여기서 .은 ? -> "현재 위치에서 react를 생성하겠다"를 의미한다.

이렇게 폴더가 생성된 것을 확인할 수 있다!

그런데, CRA는 유지보수가 되지 않는다 (마지막 업데이트가 2022년이다) 또한 빌드를 한 폴더들을 확인해보면 .js 파일이 대다수이다.

최신 트렌드 --> vite

Vite

npm create vite@latest .명령어로 입력하면 생성이 된다.
최근 트렌드는 "TypeScript + SWC" 를 선택하면 된다.

그럼 이러한 폴더들을 생성하는 것을 확인할 수 있다.
오.. vites는 처음 사용해보는데 신기하다.

CRA를 설치할 때는 npx, Vite를 설치할 때는 npm
그럼 둘의 차이는 뭘까?

NPM(Node Package Manager)

NPM은 Node Package Manager 의 약자로 Node.js의 기본 패키지 관리자이다. NPM은 Node.js 와 함께 설치되며, Node.js 애플리케이션의 종속성을 관리하고 패키지를 설치, 업데이트, 제거하는 데 사용된다.

NPX(Node Package Execute)

NPX는 Node Package Execute 의 약자로 이름 그대로 Node 패키지를 실행하는 도구이다.
Node.js를 설치하면 자동으로 설치가 된다. npx를 사용하면 npm으로 로컬 또는 글로벌로 다운로드 받지 않은 패키지도 실시간으로 설치하여 실행할 수 있게 된다.

npm은 주로 패키지 설치 및 관리를 위한 도구이고,
npx는 패키지나 명령어를 바로 실행할 때 유용한 도구이다.

YARN

YARN은 페이스북에서 개발한 패키지 매니저입니다. YARN은 더 빠르고, 더 안전하며, 더 확정적인 의존성 관리를 목표로 개발되었다.


React를 왜 사용할까 ?

"가장 인기 있는 프레임워크"이기 때문

강력한 커뮤니티

리액트는 국내외로 인기가 많은 프레임워크이기 때문에 매우 큰 커뮤니티를 보유하고 있으며, 이에 따라 풍부한 교육 자료, 튜토리얼, 예제코드, 라이브러리가 존재한다.

광범위한 생태계

리액트는 다양한 서드파티 라이브러리와 도구를 지원한다.
예를 들어서 상태 관리를 위한 리덕스, MobX, 스타일링을 위한 Styled-Components, CSS Modules, 라우팅을 위한 React Router 등이 있다.

하나를 배우면 모바일 앱까지

리액트를 배우면 React Native를 사용해서 모바일 앱을 만들 수 있다.
물론 아예 똑같은 문법은 아니지만, 같은 결의 언어이기 때문에 러닝 커브가 낮다.

돔(DOM)과 가상돔(Virtual DOM)

DOM이란?

돔은 Document Object Model 을 의미한다.
웹 브라우저에 표시되는 모든 UI 구성 요소는 웹 브라우저 내부적으로 객체 형태로 관리되고 있으며, 이를 이용해서 자바스크립트는 웹 브라우저 구성 요소에 접근할 수 있다.

이러한 DOM은 데이터 자료 구조 중 트리(tree) 구조인데, 그래서 다른 말로 돔 트리(DOM Tree)라고 하기도 한다.

가상돔(Virtual Dom)이란?

이러한 기존 DOM을 대비하기 위해서 리액트는 가상 돔이라는 새로운 개념을 만들었다.
--> 가상 돔은 기존의 DOM을 그대로 복사해서 리액트 내부 메모리에 저장한다.

리액트에서 어떤 코드의 변경 사항이 발생하게 되면 이렇게 복사한 DOM을 사용해서 변경 사항을 처리하며, 최종적으로 실제 DOM과 비교하여 변경된 부분만 업데이트하기 때문에 DOM 구성 요소의 변경에 대한 작업 비용이 낮고 효율적이다.

강력한 컴포넌트 기능

리액트를 가장 인기있는 프레임워크로 만들어 준 핵심 기능이다.

리액트는 웹의 구성 요소를 아주 작은 단위로 분할해서 재사용 가능하게 독립적인 코드로 작성한 것을 말한다. 이러한 컴포넌트를 활용하면 반복적인 웹의 구성 요소를 효과적으로 다룰 수 있다.


바벨(Babel)과 웹팩, SWC, VITE

바벨의 유레

프런트엔드에서 가장 흔하게 쓰는 바벨(babel)이라는 이름의 유래는 과거 ‘바벨탑’ 이야기에서 유래되었다. 바벨탑 이야기는 전 세계의 사람들이 모여서 세상에서 가장 높은 ‘바벨’ 이라는 이름의 탑을 만들기로 했는데, 결국 각 국의 언어가 달라서 바벨탑을 완성하지 못했다는 이야기다. 이처럼 프런트엔드에서 바벨은 서로 다른 언어 규약으로 작성된 파일을 웹 브라우저가 이해할 수 있는 형태의 파일로 변환해주는 역할을 하는 개발 툴을 일컫는다.

자바스크립트 생태계의 큰 변화, ES6 등장

2015년에 ES6(2015)가 등장하고. ES6는 과장을 조금 버태서 새로운 자바스크립트 언어라고 해도 될 정도로 기존의 ES5와 확연한 차이점을 가지게 된다.

6to5
바벨의 원래 이름은 6to5이다.
ES6를 지원하지 않는 브라우저도 ES6로 작성된 코드를 ES5로 변환해주는 툴이였던 6to5를 사용하면 문제를 해결할 수 있었다.
--> 6to5는 바벨(Babel)이라는 이름으로 재탄생하게 된다.

바벨
리액트에서도 당연히 바벨을 사용합니다. 바벨을 사용해서 리액트의 독자 코드 포맷인 JSX로 작성된 코드를 JS로 일괄적으로 변환한다.

웹팩

웹 팩은 모듈 번들러
--> 쉽게 말하면 여러 개의 자바스크립트 모듈(자바스크립트의 기능을 여러 개로 분리하여 작성한 코드)을 하나의 파일로 묶어주는 역할을 한다
--> 이때, 실제 코드에서 ‘사용하는 것만’ 자동으로 추려서 해주기 때문에 빌드 속도, 성능에 많은 영향을 준다.

Vite

웹 팩이 1세대라면 Vite는 2세대 번들러 도구를 의미한다.
--> 웹팩보다 더 빠른 속도를 장점으로 내세우고 있으며, 리액트와 궁합도 좋아서 최근에 가장 많이 사용하는 번들러 도구이다.


React 시작하기

main.tsx에서 들어가서 보게 되면,

<StrictMode>
  <App/>
</StrictMode>

<StrictMode> : React에서 제공하는 컴포넌트로, 애플리케이션을 엄격하게 모드로 실행하게 해준다. 이 모드는 개발 중에 발생할 수 있는 문제를 조기에 발견할 수 있도록 도와준다.

<App /> : 실제 애플리케이션의 루트 컴포넌트 -> 이 컴포넌트 안에 애플리케이션의 나머지 구조가 포함되어 있다.

이렇게 <StrictMode> 로 감싸면, <App /> 컴포넌트 내에서 발생하는 문제를 보다 잘 식별할 수 있도록 React가 경고를 발생시킨다.
--> 이는 개발 환경에서 유용하며, 배포된 애플리케이션에는 영향을 미치지 않는다.

클래스 컴포넌트와 함수형 컴포넌트

클래스형 컴포넌트

class App extends Component {
  render() {
    return (
      <>
        <h1>Hello World!</h1>
      </>
    );
  }
}

함수형 컴포넌트

function App() {
  return (
    <>
      <h1>Hello World!</h1>
    </>
  );
}

export default App;

이제 함수형 컴포넌트만 쓰자
16.8 부터 새롭게 추가된 ‘리액트 훅’으로 인해서 더 이상 클래스 컴포넌트를 사용하는 게 비효율적인 시대가 되었기 때문이다!


컴포넌트 CSS 스타일링

인라인 스타일(Inline Style)

인라인 스타일은 HTML 태그의 style 속성을 사용해서 CSS 스타일을 지정하는 방식을 말한다.
--> 리액트는 자바스크립트를 기본으로 하는 문법이기 때문에 style 속성으로 CSS 속성을 지정할 때 몇 가지 규칙이 있다.

  • style 속성에는 객체 형식의 값이 할당되어야 한다.
  • font-size와 같은 케밥 케이스 형식의 속성은 카멜 케이스 형식(fontSize) 로 작성해야 한다.
<h1
    style={{
    fontSize: "30px",
    color: "#ed4848",
    textDecoration: "line-through",
        }}
>
	Hello World!
</h1>

외부 스타일(External Stylesheet)

외부 스타일은 별도의 CSS 파일에 CSS 코드를 작성하고, 리액트 컴포넌트 파일과 연결해서 사용하는 방법을 말한다.

 <h1 className="title">Hello World!</h1>

App.css에서

App.css
.title {
  font-size: 30px;
  color: #ed4848;
  text-decoration: line-through;
}

작성한 후, 적용하는 것을 의미한다.

CSS Modules

모듈 방식을 사용해서 특정 컴포넌트에만 종속되는 CSS 코드를 작성하기 위한 방법이다.
외부 스타일 방법과 비슷하지만, 파일명이 *.module.css라는 특징이 있다.

폴더명을 앞에 붙이는 것이 관례이다 (App.modules.css)

import styles from './App.module.css';

 <h1 className={styles.redColor}>App Component</h1>
 <h1 className={`${styles.redColor} ${styles.underLine}`}>App Component</h1>

styles로 import를 하고, 이를 토대로 내가 App.module.css에 지정해둔 class명을 불러다가 사용하면 된다.

classnames

CSS Modules를 더 쉽게 사용하기 위해서 classnames를 사용하면 된다.

npm install classnames 로 사용하고.

import classNames from 'classnames';

 <h1 className={classNames(styles.redColor, styles.underLine)}>
    App Component
</h1>

이렇게 사용할 수 있는 것이다!

본 후기는 본 후기는 [유데미x스나이퍼팩토리] 프로젝트 캠프 : Next.js 3기 과정(B-log) 리뷰로 작성 되었습니다.
#유데미 #udemy #웅진씽크빅 #스나이퍼팩토리 #인사이드아웃 #미래내일일경험 #프로젝트캠프 #부트캠프 #React #리액트프로젝트 #프론트엔드개발자양성과정 #개발자교육과정

profile
2024. 01. 02 ~ 백앤드 공부 시작, 2024. 04.01 ~ 프론트 공부 시작

0개의 댓글