※윈도우 10 64bit, PostgreSQL Version 15.3, DBeaver 기준으로 작성된 글입니다.※

 

디비버 상단 메뉴 SQL 편집기 - SQL 편집기 또는 단축키 F3을 누르면 sql을 작성할 수 있는 script 창이 나타난다.

코드 괄호 안에서 ctrl + enter을 누르면 코드가 실행된다.

세미콜론으로 코드가 끝났는지 구분한다.

 

테이블 생성

create table 테이블명 (column1, column2, ...);

create table user_data (
	name varchar(10),
	age integer
);

 

 

테이블 수정

alter table 테이블명 rename to 새로운 테이블명;

alter table user_data rename to user_table;

 

 

테이블 삭제

drop table 테이블명;

drop table user_table;

 

 

※윈도우 10 64bit, PostgreSQL Version 15.3, DBeaver 기준으로 작성된 글입니다.※

 

DBeaver 설치하기 

https://dbeaver.io/

데이터베이스 도구가 없다면 위의 사이트에 접속한 후, Download 페이지에서 운영체제에 맞는 DBeaver를 설치한다.

 

DBeaver에서 PostgreSql 연결하기

1. 상단 메뉴의 플러그를 클릭하고, PostgreSQL을 선택한다.

 

2. Username과 Password(슈퍼유저 비밀번호)를 입력하고, Test Connection을 눌렀을 때 Connected 메시지가 나오면 연결에 성공한 것이다.

 

3. 데이터베이스 연결에 성공하면 초록색 체크박스가 나타나고, 실패하면 빨간색 엑스박스가 나타난다.

PostgreSQL(psql)이란,

PostgreSQL은 확장 가능성 및 표준 준수를 강조하는 오픈 소스 객체-관계형 데이터베이스 관리 시스템(QRDBMS)이다.

PostgreSQL은 1986년 만들어진 이후로 지금까지 활발하게 개발되고 있고, 이로 인해 안정성, 데이터 무결성, 확장성 및 강력한 성능을 가지고 있다.

소규모의 애플리케이션부터 수많은 동시 접속 사용자가 있는 대형의 애플리케이션까지 관리할 수 있고, 데이터베이스 성능은 Oracle, SQL Server와 유사하다.

MacOS 서버의 경우 PostgreSQL은 기본 데이터베이스며, Windows, Linux, BSD, Solaris와 같은 운영체제에서 엑세스할 수 있다.

 


PostgreSQL 설치하기

※윈도우 10 64bit, PostgreSQL Version 15.3 기준으로 작성된 글 입니다.

 

1. 아래 사이트에 접속한 후, Download 페이지에서 운영체제에 맞는 PostgreSQL을 설치한다.

https://www.postgresql.org/

 

2. PostgreSQL 실행 파일 다운로드가 완료되면 설치한다. 

주의: 데이터베이스 슈퍼유저 비밀번호는 PostgreSQL 데이터베이스를 만들 때 필요하기 때문에 잘 기억해야 한다.

 

3. 설치가 완료되면 SQL Shell을 실행시키고, Server, Database, Port, Username, postgres 사용자의 암호(슈퍼유저 비밀번호)를 입력한다.

암호를 제외하고 그냥 앤터만 해도 된다.

psql 버전이 나오면 정상적으로 설치가 완료됐다.

하나의 웹 사이트를 만들기 위해서는 여러 단계의 과정이 필요하다.

개발자의 관점에서 웹 사이트 기획부터 배포까지의 단계를 간단하게 설명한다.

 

  • 기획

기획자는 클라이언트의 요청에 의해 요구사항 및 필요한 기능을 정리한다.

사이트맵 작성 - 화면 설계 - 화면별 프로세스 설계 - 데이터베이스 설계

 

  • 디자인

기획자가 작성한 기획서를 바탕으로 디자이너는 레퍼런스 수집과 웹 사이트 시안을 만든다.

보통 메인 페이지와 서브 페이지 1종 시안을 만든 후, 클라이언트의 확인은 받는다.

 

  • 퍼블리싱

디자인 시안이 확정된 후, 퍼블리셔나 프론트앤드 개발자가 HTML, CSS, Jacascript로 html 파일을 만든다.

디자인 시안이 실제 웹 사이트에서 어떻게 보여지는지 확인한다.

 

  • 데이터베이스 설계 및 API

백앤드 개발자는 기획서의 요구사항에 맞춰 데이터베이스(MySQL, PostgreSQL, MariaDB, oracle 등)를 설계하고, 

GET, POST, PUT, DELETE, PATCH 등을 통해서 웹 브라우저와 웹 서버 간에 통신을 가능하게 해 준다.

 

User Data
account title
password content
name create_at
... ...

 

  • 바인딩

퍼블리싱, 데이터베이스, API 구축까지 끝나면 프론트앤드 개발자가 웹 브라우저와 웹 서버를 연결해 주는 작업을 한다.

마크업이 아닌 실제 데이터가 웹 브라우저에 보이게 된다.

 

  • 서버 및 도메인

인프라 개발자는 클라이언트의 요청에 따라 서버(aws, 가비아 등)와 도메인(url)을 구성한다.

 

  • 배포

인프라 개발자는 사용자가 실제 웹 사이트를 이용할 수 있게끔 깃 배포 자동화, FTP 등을 통해서 배포한다.

'기타 개념' 카테고리의 다른 글

REST, REST API, RESTful API란?  (0) 2023.06.08
[Node] NVM으로 노드(Node.js) 버전 관리하기  (0) 2023.04.20

03-1 타입스크립트 변수 선언문

타입스크립트 타입

let name:string = '이희재'

let age:number = 29

let isAdult:boolean = true

let a:null = null

let b:undefined = undefined // undefined은 타입이면서 값이고, undefined 값만 가질 수 있다.

// 배열
let arr1:number[] = [1, 2, 3]
let arr2:Array<number> = [1, 2, 3]

let arr3:string[] = ['hello', 'world']
let arr4:Array<string> = ['hello', 'world']

// 튜플
let tuple:[string, number]

tuple = ['hello', 100]
tuple = [10, 100] // Type 'number' is not assignable to type 'string'.

tuple[0].toLowerCase()
tuple[1].toLowerCase() // Property 'toLowerCase' does not exist on type 'number'.

// void: 함수에서 아무것도 반환하지 않을 때 사용
function sayHello():void {
  console.log('hello')
}

// never: error를 반환하거나 영원히 끝나지 않는 함수일 때 사용
function infLoop():never {
  throw new Error()

  while(true) {
	
  }
}

// enum: 자바스크립트에는 없는 타입, 아무것도 정의하지 않으면 0부터 1씩 증가하면서 할당
enum Os1 {
  Window, // 0
  Ios,    // 1
  Android // 2
}

enum Os2 {
  Window = 5,
  Ios,    // 6
  Android // 7
}

// 숫자가 아닐 때는 단방향 맵핑만 된다.
enum Os3 {
  Window = 'win',
  Ios = 'ios',
  Android = 'and'
}

console.log(Os1) // Window: 0, Ios: 1, Android: 2
console.log(Os2[5]) // Window
console.log(Os3.Window) // win

 

any 타입

any 타입은 변수의 값이 타입과 무관하게 어떤 종류의 값도 저장할 수 있다.

let random:any = '랜덤'

random = 100
random = true
random = null
random = undefined
random = [1, 2, 3]

 

undefined 타입

undefined 타입은 타입이면서 값이고, 오직 undefined 값만 가질 수 있다.

변수를 선언하고, 초기화를 하지 않으면 undefined 값을 가지는 것이 아니라 에러가 발생하기 때문에 주의해야 한다.

초기값이 없을 경우, undefined으로 선언하고 null safe(?)를 사용하는 방식으로 해결할 수도 있지만, 변수 초기화를 해주는 것이 좋다.

let num:number
console.log(num) // Variable 'num' is used before being assigned.

let num:undefined
console.log(num) // undefined

 

let과 const 키워드

  • var

var 변수는 재선언(중복 선언)되고, 재할당(업데이트) 된다.

var 변수로 선언할 경우, 유지보수의 어려움이 있어 ESNext에서 var 키워드는 사용하지 말라고 권고한다.

 

  • let

let 변수는 재할당은 가능하지만, 재선언은 불가능하다.

let으로 선언한 변수는 코드에서 그 값이 수시로 변경될 수 있음을 암시한다.

 

  • const

const 변수는 재선언, 재할당 모두 불가능하다.

const로 변수를 선언할 때는 반드시 초깃값을 명시해야 하고, 변숫값이 절대로 바뀌지 않는다.

 

타입 주석

타입 주석(type annotation)은 자바스크립트 변수 선언문을 확장해 타입을 명시하는 것을 의미한다.

let으로 선언한 변수는 값을 변경할 수 있지만, 선언된 타입과 다른 타입의 값으로 바꾸려고 하면 오류가 발생한다.

let 변수 이름: 타입
const 변수 이름: 타입 = 초깃값

let name:string = '이희재'
let age:number = 29
let isAudult:boolean

age =  30
age = '삼십' // Type 'string' is not assignable to type 'number'.

 

타입 추론

타입스크립트는 자바스크립트와 호환성을 위해 타입 주석 부분을 생략할 수 있다.

타입 추론(type inference)은 대입 연산자(=)가 오른쪽 값에 따라 변수의 타입을 지정하는 것을 의미한다.

let name = '이희재' // name을 string로 판단
let age = 29 // age를 number로 판단

 

템플릿 문자열

템플릿 문자열은 변수에 담긴 값을 조합해 문자열을 만들 수 있게 한다.

`${변수 이름}`

let count = 10
let message = 'Your count'
let result = `${message} is ${count}`

console.log(result) // Your count is 10

 


03-2 객체와 인터페이스

object 타입은 인터페이스와 클래스의 상위 타입이다.

object 타입으로 선언된 변수는 속성 이름이 다른 객체를 자유롭게 담을 수 있다.

 

아래 코드는 오류가 나지 않는 정상 코드이다.

속성값이 달라져도 오류가 발생하지 않기 때문에 오류 검출 및 유지보수를 위해서 타입 스크립트의 인터페이스 구문이 생겨났다.

let obj:object = { name: '이희재', age: 29 }
obj = { first: 1, second: 2 }

 

인터페이스 선언문

interface 키워드는 객체의 타입을 정의할 수 있다.

인터페이스를 설계할 때 필수 속성과 선택 속성이 있고, 속성 이름 뒤에 물음표 기호를 붙이면 선택 속성이 된다.

// 속성들이 여러 개일 경우, 쉼표, 세미콜론 또는 줄바꿈을 구분자로 사용한다.
interface IPerson {
  name: string
  age: number
  isAdult?: boolean // 선택 속성
  readonly birthYear: number // 읽기만 가능하고, 값을 변경할 수 없다.
}


let person1:IPerson = {
  name: '이희재',
  age: 29,
  isAdult: true,
  birtyYear: 1995
}
person1.birthYear = 2000 // Cannot assign to 'birthYear' because it is a read-only property.


Type '{ name: string; }' is missing the following properties from type 'IPerson': age, birthYear
let person2:IPerson = {
  name: '이희재'
}


// 'number' does not exist in type 'IPerson'.
let person3:IPerson = {
  name: '이희재',
  age: 29,
  number: '010-1111-1111'
}

 

익명 인터페이스

익명 인터페이스는 interface 키워드도 사용하지 않고, 인터페이스 이름도 없는 인터페이스를 의미한다.

let ai: {
  name: string
  age: number
  etc?: boolean
} = { name: 'Tom', age: 20 }

console.log(ai) // { name: 'Tom', age: 20 }

 


03-3 객체와 클래스

클래스 선언문

타입스크립트는 C++나 자바 같은 객체지향 언어에서 흔히 볼 수 있는 class, private, public, protected, implements, extend와 같은 키워드를 제공한다.

 

타입스크립트에서 생성자 변수의 타입을 선언하지 않으면 에러가 발생한다.

class Person {
  name: string
  age?: number

  // 생성자: 클래스의 속성(name, age)를 선언할 수 있다.
  constructor(name: string, age?: number) {
    this.name = name
    this.age = age
  }
}

let jack:Person = new Person()
jack.name = 'Jack'
jack.age = 32

console.log(jack) // Person { name: 'Jack', age: 32 }

 

접근 제한자

  • public

클래스 내부, 자식 클래스, 클래스 인스턴스 모두 접근이 가능하다. 아무것도 작성하지 않으면 public으로 간주한다.

 

  • protected

클래스 내부, 자식 클래스에서 접근이 가능하다.

 

  • private

클래스 내부에서만 접근이 가능하다.  캡슐화

외부에서 필요하지 않은 메서드(ex. 비밀번호 암호화 로직) 또는 임의로 변경하면 안 되는 데이터(ex. 계산기에서의 값은 계산을 통해서만 변경, 직접 값을 변경 X)를 만들 때 주로 private로 선언한다.

 

 접근 가능성  public  protected  private
 클래스 내부  O  O   O
 자식 클래스 내부  O  O  X
 클래스 인스턴스  O  X  X

 

class Car {
  public color: string // Car { color: 'black' }
  protected color: string // Property 'color' is protected and only accessible within class 'Car' and its subclasses.
  private color: string // Property 'color' is private and only accessible within class 'Car'.
  
  constructor(color: string) {
    this.color = color
  }
}

let bmw:Car = new Car()
bmw.color = 'black'

console.log(bmw)

 

인터페이스 구현

implements 키워드는 클래스가 인터페이스를 구현할 때 사용한다.

클래스에는 반드시 인터페이스가 정의하고 있는 속성을 멤버 속성으로 포함해야 한다.

 

인터페이스가 정의하고 있는 속성을 모두 포함하고 있지 않으면(name 또는 age가 하나라도 없으면) 해당 에러가 발생한다.

Class 'User' incorrectly implements interface 'IUser'.   Property 'age' is missing in type 'User' but required in type 'IUser'.

interface IUser {
  name: string
  age: number
}

class User implements IUser {
  name: string
  age: number

  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }
}

let tom:IUser = new User('Tom')

console.log(tom) // User { name: 'Tom', age: undefined }

 

추상 클래스

abstract 키워드는 추상 클래스를 정의할 때 사용하고, 직접 인스턴스를 생성할 수 없고, 상속만을 위해 사용된다.

즉, 프로퍼티나 메서드의 이름만 선언해 주고(몸체가 구현되지 않았다.), 구체적인 기능은 상속받은 쪽에서 작성할 때 사용한다.

 

클래스의 상속

extends 키워드를 사용해서 상속 클래스를 만든다.

super 키워드는 부모 클래스의 생성자를 호출할 수 있다.

abstract class Car {
  color: string
  wheel: number

  constructor(color: string, wheel: number) {
    this.color = color
    this.wheel = wheel
  }

  abstract changeColor(color: string): string
  abstract wheelCount(wheel: number): number
}

class kia extends Car {
  constructor(color: string, wheel: number) {
    super(color, wheel)
  }

  changeColor(): string {
    return 'change color'
  }

  wheelCount(): number {
    return 4
  }
}

const k5: kia = new kia('white', 4)

console.log(k5) // kia { color: 'white', wheel: 4 }
console.log(k5.changeColor()) // change color
console.log(k5.wheelCount()) // 4

 

static 속성

static 키워드는 다른 객체지향 언어처럼 클래스 정적 메서드를 정의한다. (정적인 속성을 가질 수 있다.)

정적 메서드는 인스턴스가 아닌 클래스 이름으로 호출하고, 클래스의 인터페이스를 생성하지 않아도 호출할 수 있다.

정적 메소드는 this를 사용할 수 없다.

class A {
  static first: number = 1
  second: number = 2
}

let first = A.first
let second = A.second // Property 'second' does not exist on type 'typeof A'.

console.log(first) // 1

 


03-4 객체의 비구조화 할당문

구조화

구조화(structuring)는 인터페이스나 클래스를 사용해 관련된 정보를 묶어 새로운 타입으로 표현하는 것을 의미한다..

// 구조화
export interface IPerson {
  name: string
  age: number
}

export interface ICompany {
  name: string
  age: number
}

let jack:IPerson = {
  name: 'Jack',
  age: 29
}

let naver:ICompany = {
  name: 'Naver',
  age: 10
}

console.log(jack) // { name: 'Jack', age: 29 }
console.log(naver) // { name: 'Naver', age: 10 }

 

비구조화

구조화된 데이터는 어떤 시점에서 일부만 사용해야 할 때가 있다.

이때 구조화된 데이터를 분해하는 것을 비구조화(destructuring) 라고 한다.

// 비구조화
let jack_name = jack.name
let jack_age = jack.age

console.log(jack_name, jack_age) // Jack 29

// 비구조화 할당: 중괄호로 묶어 각각의 초깃값으로 할당받는다.
let {name, age} = naver

console.log(name, age) // Naver 10

 

잔여 연산자

... 잔여 연산자를 사용하면 country와 city를 제외한 나머지 속성들이 담긴다.

let address:any = {
  country: 'Korea',
  city: 'Seoul',
  address1: '양천구',
  address2: '목동서로',
  address3: '221'
}

const {country, city, ...detail} = address

console.log(country) // 대한민국
console.log(city) // 서울
console.log(detail) // { address1: '양천구', address2: '목동서로', address3: '221' }

 

전개 연산자

... 전개 연산자를 사용하면 객체의 속성을 모두 전개해 새로운 객체로 만들어 준다.

let name = {name: 'Jack'}
let age = {age: 30}

const userInfo = {...name, ...age}

console.log(userInfo) // { name: 'Jack', age: 30 }

 


03-5 객체의 타입 변환

타입 변환(type conversion)은 특정 타입의 변숫값을 다른 타입의 값으로 변환할 수 있는 기능이다.

let person: object = {name: 'Jack', age: 32};

person.name // Property 'name' does not exist on type 'object'.

(<{name: string}>person).name // Jack

 

타입 단언

타입 단언(type assertion)은 컴파일러에게 타입을 확실히 알려주기 위해 사용하고,

강제로 타입을 지정하는 것이기 때문에 타입을 만족하지 않더라도 오류를 무시한다.

 

타입을 변경하기 위해서 as 타입 단언을 사용하지 않는 것은 좋지 않지만, 원시 타입에서 원시 타입으로 변경하거나 response(then, catch)에서 받은 값의 데이터 타입을 모를 때, as 타입 단언을 통해 데이터 타입을 주입시키는 용도로도 사용할 수 있다.

 

콜론(:)을 사용해서 타입을 지정하는 것은 타입 선언이고, 타입 선언을 이용하면 할당되는 값이 선언된 타입을 만족하는지 검사한다.

주의: react의 JSX에서 <> 꺽쇠 괄호를 사용하기 때문에 리액트에서는 as를 사용한다.

(<타입>객체)
(객체 as 타입)

export default interface INameable {
  name: string
}

let obj:object = {name: 'Jack'}

let name1 = (<INameable>obj).name
let name2 = (obj as INameable).name

console.log(typeof obj, obj) // object { name: 'Jack' }
console.log(typeof name1, name1) // string Jack
console.log(typeof name2, name2) // string Jack

 

 

참고자료

Do it! 타입스크립트 프로그래밍

버전에 맞춰서 강의와 다른 코드가 있습니다.

강의와 별개로 추가한 내용입니다.

 

결과 화면

 


redux-persist 패키지를 사용해서 데이터를 로컬 스토리지에 저장했다.

yarn add redux-persist 또는 npm install redux-persist

 

파일 구조

  • src/index.js

1. persistStore, PersistGate를 import 시킨다.

2. persistStore(store)를 만든다.

3. PersistGate 태그로 App 컴포넌트를 감싼다.

import React from 'react'
import ReactDOM from 'react-dom/client'
import { Provider } from 'react-redux'
import { persistStore } from 'redux-persist';
import { PersistGate } from 'redux-persist/integration/react';
import store from './store/store'
import App from './App'

const root = ReactDOM.createRoot(document.getElementById('root'))
const persistor = persistStore(store)

root.render(
  <Provider store={store}>
    <PersistGate loading={null} persistor={persistor}>
      <App/>
    </PersistGate>
  </Provider>
)

 

  • src/store/rootReducer.js

1. store 디렉터리에 rootReducer.js 파일을 만든다.

2. combineReducers()에 기존에 만든 reducer를 작성한다. (toDos 이외에 리듀서가 여러 개라면 쉼표로 추가해 작성한다.)

import { combineReducers } from 'redux'
import toDos from './toDoReducer'

const rootReducer = combineReducers({
  toDos
})

export default rootReducer

 

  • src/store/store.js

1. store와 reducer를 나눠서 작성한다. (store 관련 코드만 남긴다.)

2. persistReducer를 import 시키고, persistReducer()에 rootReducer를 파라미터로 가지고 온다.

3. persistConfig 변수를 만들고, configureStore()에 리듀서를 작성한다.

import { configureStore } from '@reduxjs/toolkit'
import { persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage'
import rootReducer from './rootReducer'

const persistConfig = {
  key: 'root',
  storage
}

const reducer = persistReducer(persistConfig, rootReducer)

const store = configureStore({
  reducer
})

export default store

 

  • src/store/toDoReducer.js

1. store와 reducer를 나눠서 작성한다. (reducer관련 코드만 남긴다. 파일명이 헷갈릴 수 있으니 리네이밍한다.)

import { createSlice } from '@reduxjs/toolkit'

const todoSlice = createSlice({
  name: 'reducerToDo',

  initialState: {
    toDoList: []
  },

  reducers: {
    addToDo: (state, action) => {
      state.toDoList.unshift({ id: Date.now(), text: action.payload })
    },
    deleteToDo: (state, action) => {
      state.toDoList = state.toDoList.filter(list => list.id !== action.payload)
    }
  }
})

export const { addToDo, deleteToDo } = todoSlice.actions
export default todoSlice.reducer

 

  • src/routes/Home.js

1. useSelector()로 state를 가지고 올 때, 로컬 스토리지에 있는 데이터(state.toDos.toDoList)를 가지고 온다.

import React, { useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { addToDo } from '../store/toDoReducer'
import ToDo from '../components/ToDo'
import './home.scss'

function Home() {
  // ** Hooks
  const dispatch = useDispatch()

  // ** States
  const [text, setText] = useState('')

  // ** Redux States
  const toDos = useSelector(state => state.toDos.toDoList)

  function onChange(e) {
    setText(e.target.value)
  }

  function onSubmit(e) {
    e.preventDefault()
    dispatch(addToDo(text))
    setText('')
  }

  return (
    <>
      <div className="layout home">
        <h1>To Do List</h1>

        <form onSubmit={onSubmit}>
          <input type="text" value={text} onChange={onChange} placeholder="What is your to do?"/>
          <button>Add</button>
        </form>

        <ul>
          {
            toDos.map((toDo, index) => {
              return (
                <ToDo id={toDo.id} text={toDo.text} key={index}/>
              )
            })
          }
        </ul>
      </div>
    </>
  )
}

export default Home

 

 

자세한 소스코드는 아래 깃 레포지토리를 참고한다.

https://github.com/heejae0811/react-todo

02-1 타입스크립트 프로젝트 만들기

타입스크립트 개발은 nodejs 프로젝트를 만든 다음, 개발 언어를 타입스크립트로 설정하는 방식으로 진행한다.

 

프로젝트 생성자 관점에서 패키지 설치하기

1. 폴더 만들기

2. 만든 폴더로 이동 후, 터미널에서 npm init (package.json 파일이 생긴다.)

3. npm install -D typescript ts-node (node_modules 디렉터리가 생기고, 각 패키지의 디렉터리들을 확인할 수 있다.)

 

프로젝트 이용자 관점에서 패키지 설치하기

프로젝트를 구현할 때 여러 패키지를 설치하게 되므로 node_modules 디렉터리의 크기가 매우 커진다. 그래서 다른 사람들에게 프로젝트를 전달할 때 node_modules 폴더를 모두 지운다. package.json 파일이 있는 디렉터리의 터미널에서 npm install 또는 npm i 명령어를 실행하면 package.json에 등록된 패키지들이 node_modules 디렉터리에 자동으로 설치된다.

// package.json
{
  "name": "ch02-1",
  "version": "1.0.0",
  "description": "",
  "main": "src/index.js",
  "scripts": {
    "dev": "ts-node src",
    "build": "tsc && node dist"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@types/chance": "^1.1.3",
    "@types/node": "^20.1.0",
    "@types/ramda": "^0.29.1",
    "ts-node": "^10.9.1",
    "typescript": "^5.0.4"
  },
  "dependencies": {
    "chance": "^1.1.11",
    "ramda": "^0.29.0"
  }
}

 

tsconfig.json 파일 만들기

tsc --init 명령어를 실행하면 타입스크립트 컴파일러의 설정파일인 tsconfig.json 파일이 만들어진다.

// tsconfig.json
{
  "compilerOptions": {
    "module": "commonjs",
    "moduleResolution": "node",
    "target": "es5",
    "baseUrl": ".",
    "outDir": "dist",
    "paths": {
      "*": [
        "node_modules/*"
      ]
    },
    "esModuleInterop": true,
    "sourceMap": true,
    "downlevelIteration": true,
    "noImplicitAny": false,

  },
  "include": [
    "src/**/*"
  ]
}

 


02-2 모듈 이해하기

타입스크립트에서는 index.ts와 같은 소스 파일을 모듈(module)이라고 한다.

소스 파일을 하나로 구현해도 되지만, 코드 관리와 유지보수를 편리하기 하기 위해서 모듈마다 고유한 기능을 구현하는 방식으로 소스코드를 분할한다. 이러한 작업을 모듈화(modulization) 라고 한다.

 

export 키워드

작성한 소스코드를 다른 파일에서 동작하게 하려면 export 키워드로 심벌을 내보낸다.

export 키워드는 interface, class, type, let, const 키워드 앞에도 붙일 수 있다.

 

export default 키워드

export default 키워드는 한 모듈이 내보내는 기능 중 오직 1개에만 붙일 수 있다.

export default가 붙은 기능은 import 문으로 불러올 때 중괄호 없이 사용할 수 있다.

export default interface IPerson {
  name: string,
  age: number
}

 

import 키워드

export 키워드로 내보낸 심벌을 사용하기 위해서 import 키워드로 해당 심벌을 불러온다.

import { 심벌목록 } from '파일 상대 경로'
import * as 심벌 from '파일 상대 경로'

import IPerson from './IPerson'

 

외부 패키지를 사용할 때 import 문

chance는 가짜 데이터를 만들어 주는 패키지이다.

ramda는 함수형 유틸리티 패키지이다.

npm i -S chance ramda
npm i -D @types/chance @types/ramda

import Chance from 'chance'
import * as R from 'ramda'

 


02-3 tsconfig.json 파일 살펴보기

콜론(:)을 기준으로 "키:키값" 형태로 작성한다.

// tsconfig.json
{
  "compilerOptions": {
    "module": "commonjs",
    "moduleResolution": "node",
    "target": "es5",
    "baseUrl": ".",
    "outDir": "dist",
    "paths": {
      "*": [
        "node_modules/*"
      ]
    },
    "esModuleInterop": true,
    "sourceMap": true,
    "downlevelIteration": true,
    "noImplicitAny": false,

  },
  "include": [
    "src/**/*"
  ]
}

 

  • compilerOptions

tsc 명령 형식에서 옵션을 나타낸다.

 

  • include

compilerOptions의 대상 파일 목록이다.

 

  • module

타입스크립트 소스코드가 컴파일되어 만들어진 자바스크립트 소스코드는 웹 브라우저와 nodejs 모두 동작해야 한다. 그런데 웹 브라우저와 nodejs는 물리적으로 동작하는 방식이 달라서 자바스크립트 코드 또한 웹 브라우저랑 nodejs에서 다르게 동작한다. 따라서 tsconfig.js 파일에서 module 키는 동작 대상이 웹 브라우저인지 nodejs인지 구분해줘야 한다.

 

웹 브라우저에서 동작: amd

nodejs에서 동작: commonjs

 

  • moduleResolution

module의 키 값이 amd이면 moduleResolution 키 값은 classic

module의 키 값이 commonjs이면 moduleResolution 키 값은 node

 

  • target 키

트랜스파일할 대상 자바스크립트의 버전을 설정한다.

 

  • baseUrl

주로 현재 디렉터리를 의미하는 "."으로 키 값을 설정한다.

 

  • outUrl

baseUrl 설정값을 기준으로 했을 때 하위 디렉터리의 이름이다.

빌드된 결과가 해당 디렉터리에 만들어진다.

 

  • paths

import 문에서 from 부분을 해석할 때 찾아야 하는 디렉터리이다.

 

  • esModuleInterop

chance 패키지를 사용하려면 true로 설정한다.

 

  • sourceMap

sourceMap의 값이 true이면 트랜스파일 디렉터리에 js 파일뿐만 아니라. js.map 파일도 만들어진다.

 

  • downlevelIteration

생성기 구문을 사용하려면 true로 설정한다.

 

  • noImplicitAny

타입을 지정하지 않더라도 오류로 인식하지 않게 하려면 false로 설정한다.

 

 

참고자료

Do it! 타입스크립트 프로그래밍

01-1 타입스크립트란 무엇인가?

세 종류의 자바스크립트

  • 웹 브라우저에서 동작하는 표준 자바스크립트 ES5(ECMAScript 5)
  • 2015년부터 매년 새로운 버전을 발표하는 ESNext(ES6 이후의 버전)
  • ESNext에 타입(type) 기능을 추가한 타입스크립트(TypeScript)

자바스크립트(동적 언어): 런타임에 타입 결정, 오류 발견

타입스크립트, 자바(정적 언어): 컴파일 타임에 타입 결정, 오류 발견

 

타입스크립트는 누가 만들었나?

타입스크립트는 마이크로소프트가 개발하고 유지하고 있는 오픈소스 프로그래밍 언어로 2012년 말 처음 발표됐다.

구글의 Anglar.js 팀이 앵귤러 버전 2를 타입스크립트로 만들면서 널리 알려졌고, 앵귤러뿐만 아니라 리액트나 뷰 프레임워크에서도 타입스크립트를 사용해 개발되고 있다.

 

자바스크립트에 타입 기능이 있으면 좋은 이유

여러 사람이 협력해 하나의 제품을 개발하기 때문에 다른 개발자가 만들어 놓은 코드를 이용하려고 했을 때 오류가 발생했다면, 오류의 원인이 무엇인지 찾기가 어렵다. 타입스크립트 컴파일러는 문제의 원인이 어디에 있는지 친절하게 알려주기 때문에 코드를 좀 더 수월하게 작성할 수 있다. 따라서 많은 개발자들이 대규모 소프트웨어를 만들 때 타입스크립트를 선호하게 되었다.

 

  • 타입스크립트 소스 코드

타입스크립트를 사용 시, 소스 코드를 실행하기 전에 오류를 알려줘 개발할 때 시간이 오래 걸리더라도 오류 검출이나 유지보수에 용이하다.

아래 사진처럼 컴파일하기 전에 어디에 오류가 있는지 알려준다.

 

 

  • 자바스크립트 소스 코드

타입스크립트와 달리 자바스크립트는 오류 없이 소스 코드를 실행하기 때문에 NaN과 같은 원하지 않는 결과가 나오거나, 컴파일 시 TypeError를 알려준다.

이렇게 되면 오류 검출에 많은 시간을 소요하게 되기 때문에 타입스크립트를 사용하는 것이다.

 

 

트랜스파일

트랜스파일러(transpiler)란, 어떤 프로그래밍 언어로 작성된 소스코드를 또 다른 프로그래밍 언어로 바꿔주는 프로그램을 의미한다. (컴파일과 동일한 뜻으로 사용)

즉, 우리가 사용하는 웹 브라우저는 타입스크립트를 이해하지 못하기 때문에 트랜스파일러를 이용해서 타입스크립트를 자바스크립트로 변환해주어야 한다.

 

ESNext 자바스크립트 소스코드는 바벨이라는 트랜스파일러를 통해 ES5 자바스크립트 코드로 변환된다.

타입스크립트 소스코드는 TSC(TypeScript Compiler)라는 트랜스파일러를 통해 ES5 자바스크립트 코드로 변환된다.

 


01-2 타입스크립트의 주요 문법 살펴보기

ESNext의 주요 문법 살펴보기

타입스크립트는 ESNext 문법을 지원하기 때문에 타입스크립트를 다루기 위해서는 ESNext 문법을 알아야만 한다.

 

1. 비구조화 할당

객체와 배열에 비구조화 할당을 적용할 수 있다.

// 객체
let person = { name: 'Jane', age: 22 }
let { name, age } = person // name = 'Jane', age = 22

// 배열
let array = [1, 2, 3, 4]
let [head, ...rest] = array // head = 1, rest = [2, 3, 4]

 

2. 화살표 함수

function 키워드 대신에 화살표(=>)로 함수를 선언할 수 있다.

function add(a, b) { return a + b }
const add = (a, b) => a + b

 

3. 클래스

객체지향 프로그래밍을 지원한다.

캡슐화, 상속, 다형성

 

4. 모듈

모듈을 사용하면 코드를 여러 개의 파일로 분할해서 작성할 수 있다.

변수, 함수, 클래스 등에 export 키워드를 사용해 모듈로 만들면 다른 파일에서도 사용할 수 있고, 모듈을 사용하고 싶을 때는 import 키워드를 사용한다.

 

5. 생성기

yield 문은 반복자를 의미하는 반복기를 생성할 때 사용한다.

반복기는 독립적으로 존재하지 않고 반복기 제공자(생성기)를 통해 얻는다.

 

6. Promise와 async/await 구문

비동기 콜백 함수를 상대적으로 쉽게 구현할 목적으로 만들어졌다.

 

타입스크립트 고유의 문법 살펴보기

1. 타입 주석과 타입 추론

변수 뒤의 콜론과 타입 이름을 "타입 주석" 이라고 한다.

변수의 타입 부분이 생략되면 대입 연산자의 오른쪽 값을 분석해 왼쪽 변수의 타입을 결정하는 것을 "타입 추론" 이라고 한다.

let n: number = 1

 

2. 인터페이스

interface Person {
    name: string,
    age?: number
}

let person: Person = { name: "Jane" }

 

3. 튜플

배열에 저장되는 아이템의 데이터 타입이 모두 같으면 배열, 다르면 "튜플"이다.

let array: number[] = [1, 2, 3]
let tuple: [boolean, number, string] = [true, 1, 'ok']

 

4. 제네릭 타입

여러 가지 타입을 대상으로 동작할 수 있는 코드를 "제네릭 타입" 이라고 한다.

 

5. 대수 타입

대수 타입이란, 다른 자료형의 값을 가지는 자료형을 의미한다.

대수 타입에는 합집합 타입(&), 교집합 타입(|) 2가지가 있다.

type NumberOrString = number | string
type AnimalAndPerson = Animal & Person

 


01-3 타입스크립트 개발 환경 만들기

타입스크립트의 개발 환경은 nodejs 개발 환경과 동일하다. (타입스크립트는 nodejs 환경에서만 동작한다. = node가 설치되어 있어야 한다.)

nodejs와 웹 브라우저, 에디터만 있으면 개발할 수 있다.

 

타입스크립트 컴파일러 설치

npm: nodejs 패키지 관리자

// 1. 타입스크립트 컴파일러 설치
npm i -g typescript

// 1-1. 설치된 타입스크립트 컴파일러 버전 확인
tsc --version 또는 tsc -v

// 2. 타입스크립트 코드를 ES5로 변환하고, 실행까지 시키는 패키지 설치
npm i -g ts-node

// 2-1. 설치된 ts-node 버전 확인
ts-node --version 또는 ts-node -v

 

tsc hello.ts 명령어를 실행하면 타입스크립트 소스가 tsc에 의해 트랜스파일 되어 hello.js 파일이 생성됐다.

 

 

 

참고자료

Do it! 타입스크립트 프로그래밍

버전에 맞춰서 강의와 다른 코드가 있습니다.

 

결과 화면

 


Redux Toolkit 기본 개념

  • configureStore()

기존 createStore 역할을 대신해 사용한다.

configureStore()는 리듀서 조각들을 자동으로 합쳐주고, 기본으로 제공되는 기능뿐만 아리나 Redux DevTools 확장을 사용할 수 있게 해준다.

 

  • createSlice()

기존 reducer에서 switch문 역할을 대신해 사용한다.

초기 state와 리듀서 함수를 작성할 수 있고, array.push와 같은 state를 직접 수정할 수 있다.

state는 mutate 하면 안되는 것이 원칙인데, 리덕스 툴깃이 자동으로 새로운 state로 반환시켜준다.

 


 

redux toolkit을 사용하기 위해 터미널에서 yarn add @reduxjs/toolkit 또는 npm install @reduxjs/toolkit 명령어르 실행한다.

 

파일 구조

  • src/store/store.js

store를 만든다.

import { configureStore } from '@reduxjs/toolkit'
import { persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage'
import rootReducer from './rootReducer'

const persistConfig = {
  key: 'root',
  storage
}

const reducer = persistReducer(persistConfig, rootReducer)

const store = configureStore({
  reducer
})

export default store

 

  • src/store/rootReducer.js

rootRedecer를 만든다.

toDos 이외의 reducer를 만들게 되면 rootReducer에 추가한다.

import { combineReducers } from 'redux'
import toDos from './toDoReducer'

const rootReducer = combineReducers({
  toDos
})

export default rootReducer

 

  • src/store/toDoReducer.js

reducers 안에 액션을 만든다.

initialState의 toDoList에 toDo를 추가하고, 삭제하는 기능을 만들기 위해 addToDo와 deleteToDo를 만든다.

addToDo 액션에서 unshift()를 통해 배열에 새로운 toDo를 추가하고,

deleteToDo 액션에서 filter()를 통해 id값이 일치하지 않는 나머지 값들만 return해 toDo를 삭제한다.

import { createSlice } from '@reduxjs/toolkit'

const todoSlice = createSlice({
  name: 'reducerToDo',

  initialState: {
    toDoList: []
  },

  reducers: {
    addToDo: (state, action) => {
      state.toDoList.unshift({ id: Date.now(), text: action.payload })
    },
    deleteToDo: (state, action) => {
      state.toDoList = state.toDoList.filter(list => list.id !== action.payload)
    }
  }
})

export const { addToDo, deleteToDo } = todoSlice.actions
export default todoSlice.reducer

 

  • src/routes/Home.js

useSelector()로 스토어에 있는 todoList state를 가지고 온다.

dispatch를 통해 reducers에 만들어 놓은 액션을 가지고 온다.

import React, { useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { addToDo } from '../store/store'
import ToDo from '../components/ToDo'
import './home.scss'

function Home() {
  // ** Hooks
  const dispatch = useDispatch()

  // ** States
  const [text, setText] = useState('')

  // ** Redux States
  const toDos = useSelector(state => state.todoList)

  function onChange(e) {
    setText(e.target.value)
  }

  function onSubmit(e) {
    e.preventDefault()
    dispatch(addToDo(text))
    setText('')
  }

  return (
    <>
      <div className="layout home">
        <h1>To Do List</h1>

        <form onSubmit={onSubmit}>
          <input type="text" value={text} onChange={onChange} placeholder="What is your to do?"/>
          <button>Add</button>
        </form>

        <ul>
          {
            toDos.map((toDo, index) => {
              return (
                <ToDo id={toDo.id} text={toDo.text} key={index}/>
              )
            })
          }
        </ul>
      </div>
    </>
  )
}

export default Home

버전에 맞춰서 강의와 다른 코드가 있습니다.

강의와 별개로 추가한 내용입니다.

 

결과 화면

 


scss를 사용하기 위해 터미널에서 yarn add node-sass 또는 npm install node-sass 명령어를 실행한다.

 

파일 구조

  • src/scss/layout/_layout.scss
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans:wght@400;700&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;700&display=swap');

body {
    background-color: burlywood;
    font-family: 'Noto Sans', 'Noto Sans KR', sans-serif;

    .layout {
        position: relative;
        top: 0;
        left: 0;
        bottom: 0;
        right: 0;
        max-width: 500px;
        width: 90%;
        margin: 50px auto;
        padding: 30px;
        background-color: bisque;

        button {
            min-height: 30px;
            padding: 0 10px;
            background-color: #fff;
        }
    }
}

 

  • src/routes/home.scss
.home {
    h1 {
        margin-bottom: 30px;
        text-align: center;
    }

    form {
        display: flex;
        justify-content: space-between;
        gap: 10px;
        margin-bottom: 10px;

        input {
            width: 100%;
            height: 30px;
            padding: 10px;
        }
    }
}

 

  • src/components/todo.scss
ul {
    li {
        display: flex;
        justify-content: space-between;
        align-items: center;
        margin-bottom: 10px;

        &:last-child {
            margin-bottom: 0;
        }
    }
}

 

  • src/routes/detail.scss
.detail {
    h1 {
        margin-bottom: 30px;
        text-align: center;
    }

    .date {
        margin-bottom: 10px;
        text-align: right;
    }
}

+ Recent posts