05-1 배열 이해하기

자바스크립트와 타입스크립트에서 배열은 다른 언어와 다르게 객체이다.

 

  • new Array 배열 선언하기

new Arrar 키워드를 통해 배열을 만들 수 있고, 배열에 담긴 값을 아이템(item) 또는 원소(element) 라고 한다.

let arr: number[] = new Array

arr.push(1)
arr.push(2)
arr.push(3)

console.log(arr) // [1, 2, 3]

 

  • [] 단축 구문 배열 선언하기

[] 단축 구문을 사용해서 배열을 만들 수 있다.

// [] 단축 구문
const numArr: number[] = [1, 2, 3]
const strArr: string[] = ['hello', 'world']

console.log(num) // [1, 2, 3]
console.log(str) // ['hello', 'world']

 

문자열과 배열 간 변환

타입스크립트에서는 문자 타입이 없고, 문자열의 내용 또한 변경할 수 없다.

따라서 문자열을 변경하기 위해서는 문자열을 배열로 변환해야 한다.

 

let str: string = 'hello'
let strArr: string[] = str.split('')

console.log(strArr.join('-')) // h-e-l-l-o

 

인덱스 연산자

인덱스 연산자, [인덱스]는 배열의 특정 위치에 있는 값을 알 수 있다.

let numbers: number[] = [1, 2, 3]

for(let i = 0; i < numbers.length; i++) {
    console.log(numbers[i])
}

// 1 2 3

 

배열의 비구조화 할당

객체뿐만 아니라 배열에도 비구조화 할당을 적용할 수 있다.

비구조화 할당이란, 배열이나 객체 속성을 해체하여 그 값을 변수에 담을 수 있게 하는 표현식이다.

let arr: number[] = [1, 2, 3, 4, 5]
let [first, second, third, ...rest] = arr

console.log(first, second, third, rest) // 1 2 3 [ 4, 5 ]

 

for ...in 문

배열의 인덱스 값을 순회한다.

let names: string[] = ['다연', '하영', '희재']

for(let index in names) {
    console.log(`[${index}]: ${names[index]}`)
}

// [0]: 다연 [1]: 하영 [2]: 희재

 

for ...of 문

배열의 아이템 값을 대상으로 순회한다. (인덱스값을 알 수 없다.)

let names: string[] = ['다연', '하영', '희재']

for(let index of names) {
    console.log(index)
}

// 다연 하영 희재

 

제네릭 방식 타입

배열을 다루는 함수를 작성할 때는 고정된 타입을 함수를 만들기보다는 T[] 형태(제네릭 타입)로 배열의 아이템 타입을 한꺼번에 표현하는 것이 편리하다.

타입스크립트는 타입 변수가 생략된 제네릭 함수를 만나면 타입 추론을 통해서 생략된 타입을 찾아낸다.

 

제네릭 사용 이유
  1. 한 가지 타입보다 여러 가지 타입에서 동작하는 컴포넌트를 생성할 수 있다.
  2. 재사용성이 높은 함수와 클래스를 생성할 수 있다.
  3. any 타입을 사용하는 대신에 사용하기 때문에 오류를 쉽게 검출할 수 있다.

 

function sort<T>(item: T[]): void {
    console.log(item.sort())
}
 
const nums: number[] = [3, 2, 5, 4, 1];
const chars: string[] = ['d', 'a', 'e', 'c', 'b'];
 
sort<number>(nums) // [ 1, 2, 3, 4, 5 ]
sort<string>(chars) // [ 'a', 'b', 'c', 'd', 'e' ]

 

전개 연산자

배열에서도 전개 연산자를 사용할 수 있다.

const arr1: number[] = [1, 2, 3]
const arr2: string[] = ['a', 'b', 'c']
const arr3: (number | string)[] = [...arr1, ...arr2]

console.log(arr3) // [ 1, 2, 3, 'a', 'b', 'c' ]

 

range 함수 구현

range 함수는 재귀 함수 스타일로 동작하고, 배열을 생성해 준다.

const range = (from: number, to: number): number[] => {
    return from < to ? [from, ...range(from + 1, to)] : []
}

let numbers: number[] = range(1, 5)

console.log(numbers) // [ 1, 2, 3, 4 ]

 


05-2 선언형 프로그래밍과 배열

명령형 프로그래밍

ex) for 문

  • 입력 데이터 얻기
  • 입력 데이터 가공해 출력 데이터 생성
  • 출력 데이터 출력

 

선언형 프로그래밍

ex) for 문을 사용하지 않고 모든 데이터를 배열에 담는 형식

  • 문제를 푸는 데 필요한 모든 데이터 배열에 저장
  • 입력 데이터 배열을 가공해 출력 데이터 배열 생성
  • 출력 데이터 배열에 담긴 아이템 출력

 

1부터 100까지 더하기 

  • 명령형 프로그래밍
let sum = 0

for(let i = 1; i <= 100; i++) {
    sum = sum + i
}

console.log(sum) // 5050

 

  • 선언형 프로그래밍
const range = (from: number, to: number): number[] => {
    return from < to ? [from, ...range(from + 1, to)] : []
}

const numbers: number[] = range(1, 101)

console.log(numbers.reduce((acc, cur) => acc + cur)) // 5050

 


05-3 배열의 map, reduce, filter 메서드

filter 메서드

깊은 복사의 형태로 동작한다. (= 원본 배열을 변경하지 않는다.)

const arr: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

const odd: number[] = arr.filter((item, index) => item % 2 !== 0)
const even: number[] = arr.filter((item, index) => item % 2 === 0)

console.log(odd) // [ 1, 3, 5, 7, 9 ]
console.log(even) // [ 2, 4, 6, 8, 10 ]

 

map 메서드

깊은 복사의 형태로 동작한다. (= 원본 배열을 변경하지 않는다.)

const arr: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

const double = arr.map((item, index) => item * 2)

console.log(double) // [2,  4,  6,  8, 10, 12, 14, 16, 18, 20]

 

reduce 메서드

const arr: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

const total = arr.reduce((acc, cur) => acc + cur)

console.log(total) // 55

 


05-4 순수 함수와 배열

함수형 프로그래밍에서 함수는 '순수 함수'라는 조건을 만족해야 한다.

그러나 타입스크립트의 배열에는 순수 함수 조건에 부합하지 않는 메서드들이 많다.

 

순수 함수란?

'순수 함수'는 부수 효과(side-effect)가 없는 함수를 의미한다. (부수 효과가 있으면 '불순 함수' 라고 한다.)

 

  • 함수 몸통에 입출력에 관련된 코드가 없어야 한다.
  • 함수 몸통에서 매개변숫값을 변경시키지 않는다. (즉, 매개변수는 const나 readonly 형태로만 사용한다.)
  • 함수는 몸통에서 만들어진 결과를 즉시 반환한다.
  • 함수 내부에 전역 변수나 정적 변수를 사용하지 않는다.
  • 함수가 예외를 발생시키지 않는다.
  • 함수가 콜백 함수로 구현되었거나 함수 몸통에 콜백 함수를 사용하는 코드가 없다.
  • 함수 몸통에 Promise와 같은 비동기 방식으로 작동하는 코드가 없다.

 

  • 순수 함수
function pure(a: number, b: number): number {
    return a + b
}

console.log(pure(1, 2)) // 3

 

  • 불순 함수

매개변수 arr이 변경된다.

function impure(arr: number[]): void {
    console.log(arr) // []
    
    arr.push(100)
    
    console.log(arr) // [100]
}

impure([])

 

타입 수정자 readonly

타입스크립트는 순수 함수 구현을 쉽게 하도록 readonly 키워드를 제공한다.

또는 인터페이스, 클래스, 함수의 매개변수는 let, const 키워드 없이 선언하기 때문에 심벌에 const와 같은 효과를 주기 위해서 readonly 타입 수정자가 필요하다.

function impure(arr: readonly number[]): void {
    // Property 'push' does not exist on type 'readonly number[]'.
    arr.push(100)
}

 

불변과 가변

  • 불변 변수

const 또는 readonly 키워드로 선언된 변수이다.

초기값을 항상 유지한다.

 

  • 가변 변수

var 또는 let 키워드로 선언된 변수이다.

언제든 변수의 값을 변경할 수 있다.

 

깊은 복사와 얕은 복사

순수 함수를 구현할 때는 매개변수가 불변성을 유지해야 한다. 매개변수를 가공하려고 할 때 깊은 복사를 실행해 매개변숫값이 변경되지 않게 해야 한다.

그러나 새로운 변수에 기존 변수를 할당하는 방식으로 복사를 시도하면 객체와 배열은 얕은 복사 방식으로 동작하기 때문에 주의해야 한다.

 

  • 깊은 복사

원본 변수의 값이 변경되지 않는다.

let original = 1
let copy = original

copy = copy + 2

console.log(original) // 1
console.log(copy) // 3

 

  • 얕은 복사

원본 배열의 값이 변경된다.

let original = [1, 2, 3]
let copy = original

cop.push(4)

console.log(original) // [1, 2, 3, 4]
console.log(copy) // [1, 2, 3, 4]

 

전개 연산자와 깊은 복사

전개 연산자를 사용해 객체와 배열을 깊은 복사할 수 있다.

let originArr = [1, 2, 3]
let copyArr = [...originArr]

copyArr.push(4)

console.log(originArr) // [ 1, 2, 3 ]
console.log(copyArr) // [ 1, 2, 3, 4 ]

 

배열의 sort 메서드를 순수 함수로 구현하기

sort 메서드는 원본 배열의 내용을 변경한다. 

따라서 원본 배열을 유지한채 sort 메서드를 사용하고 싶다면 전개 연산자의 깊은 복사 기능을 먼저 사용하면 된다.

 

  • sort 얕은 복사

원본 배열의 값이 변경된다.

let arr: number[] = [3, 5, 2, 1, 4]
let sortArr: number[] = arr.sort()

console.log(arr) // [ 1, 2, 3, 4, 5 ]
console.log(sortArr) // [ 1, 2, 3, 4, 5 ]

 

  • sort 깊은 복사

원본 배열의 값이 변경되지 않는다.

let arr: number[] = [3, 5, 2, 1, 4]
let sortArr: number[] = [...arr].sort()

console.log(arr) // [ 3, 5, 2, 1, 4 ]
console.log(sortArr) // [ 1, 2, 3, 4, 5 ]

 

배열의 filter 메서드와 순수한 삭제

보통 배열에서 특정 아이템을 삭제할 때 splice 메서드를 많이 사용하는데 splice 메서드는 원본 배열을 변경하므로 순수 함수에서는 사용할 수 없다.

filter 메서드는 깊은 복사 형태로 동작하기 때문에 원본 배열을 유지한 채 특정 아이템을 삭제할 수 있다.

interface Data {
  id: number
  title: string
}

const data: Data[] = [
  { id: 1, title: '제목1' },
  { id: 2, title: '제목2' },
  { id: 3, title: '제목3' }
]

let filterData = data.filter((item, index) => item.id !== 1)

console.log(data) // [ { id: 1, title: '제목1' }, { id: 2, title: '제목2' }, { id: 3, title: '제목3' } ]
console.log(filterData) // [ { id: 2, title: '제목2' }, { id: 3, title: '제목3' } ]

 

가변 인수 함수와 순수 함수

가변 인수(variadic arguments)는 함수를 호출할 때 전달하는 인수의 개수를 제한하지 않는다.

 

  1. 가변 인수로 호출할 때 타입에 상관없이 동작하게 하기 위해서 제네릭 타입으로 구현한다.
  2. 전개 연산자를 사용해 가변 인수를 표현한다.
  3. 가변 인수로 전달하는 값이 배열이기 때문에 타입을 배열의 배열로 선언하고, 매개변수는 변경되면 안되기 때문에 readonly로 선언한다.

 

const mergeArr = <T>(...arr: readonly T[][]): T[] => {
  let result: T[] = []

  for(let i = 0; i < arr.length; i++) {
    const array: T[] = arr[i]
    result = [...result, ...array]
  }

  return result
}

console.log(mergeArr([1, 2, 3])) // [1, 2, 3]
console.log(mergeArr(['hello', 'world'])) // [ 'hello', 'world' ]
console.log(mergeArr([{name: '희재', age: 27}])) // [ { name: '희재', age: 27 } ]

 

순수 함수를 고려하면 사실상 자바스크립트 배열이 제공하는 많은 메서드를 사용할 수 없지만,
전개 연산자 등의 메커니즘을 사용하면 순수 함수 형태로 간단하게 표현될 수 있음을 알아야 한다.

 


05-5 튜플 이해하기

튜플이란, 길이와 타입이 고정된 배열이다. 배열에 담긴 데이터를 만들 때 순서를 무시하고 만들게 되는 오류를 방지할 수 있다.

또한 튜플은 물리적으로 배열이기 때문에 배열처럼 인덱스 연산자나 비구조화 할당문을 적용할 수 있다.

// 타입 별칭으로 튜플의 의미를 정확하게 한다.
type tupleType = [number, string]

const tuple1: tupleType = [1, 'title1']
const tuple2: tupleType = ['title2', 2] // Type 'string' is not assignable to type 'number'.

console.log(tuple1[0], tuple1[1]) // 1, title1 (인덱스 연산자 사용)

// 비구조화 할당
let [id, title] = tuple1 
console.log(id, title) // 1, title1

 

그러나 튜플의 의미와 맞지 않게 push 메서드를 사용해 배열의 길이를 늘릴 수 있어 주의해야 한다. 

type tupleType = [number, string]

const tuple: tupleType = [1, 'title']

tuple.push('content')
tuple.push(true) // Argument of type 'boolean' is not assignable to parameter of type 'string | number'.

console.log(tuple) // [ 1, 'title', 'content' ]

 

 

 

참고자료

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

결과 화면

 


1. styled-components, @types/styled-components 설치하기

styled-components 패키지와 @types/styled-components 패키지를 설치한다.

 

자바스크립트일 경우, styled-components 패키지만 설치해도 되지만,

타입스크립트에서 사용하기 위해서는 @types/styled-components 패키지도 설치해야 한다.

yarn add styled-components
또는
npm install styled-components
yarn add @types/styled-components
또는
npm install @types/styled-components

 

2. Button 컴포넌트 만들기

2-1. StyledButton 만들기

  • src/components/Button.tsx

styled-components를 import 한다.

StyledButton 컴포넌트를 만든 후, `` 안에 css를 작성한다.

StyledButton 컴포넌트를 적용할 Button 컴포넌트를 만든다. 

import styled from 'styled-components'

const StyledButton = styled.button`
  // 필요한 css를 작성한다.
  
  width: 100%;
  height: 50px;
  ...
`

const Button = () => {
  return (
    <StyledButton>버튼</StyledButton>
  )
}

export default Button

 

2-2. StyledButton에 props 적용하기

props로 값을 받기 위해서는 ${(props) => props.~} 형태로 받아야 한다.

 

backgroundColor 변수를 만들고, 필요한 값을 작성한다. (background-color가 필요했을 뿐, 다른 값들도 가능하다.)

props로 넘길 값(backgroundColor)의 타입을 정의하기 위해 interface를 만든다.

StyledButton 컴포넌트에 타입(ButtonProps)과 props로 넘길 값을 정의한다.

Button 컴포넌트의 파라미터에 variant 값을 넘긴다.

import styled from 'styled-components'

const backgroundColor = {
  primary: '#677987',
  secondary: '#B4B6AC'
}

interface ButtonProps {
  readonly variant: 'primary' | 'secondary'
}

const StyledButton = styled.button<ButtonProps>`
  width: 100%;
  height: 50px;
  background-color: ${(props) => backgroundColor[props.variant]};
  text-align: center;
`

const Button = ( { variant }: ButtonProps) => {
  return (
    <StyledButton variant={variant}>버튼</StyledButton>
  )
}

export default Button

 

2-3. 부모 컴포넌트에 값 전달하기

Button 컴포넌트의 텍스트도 props로 전달하기 위해서 children을 사용한다.

 

interface에 children의 타입을 정의한다.

Button 컴포넌트의 파라미터에 childred 값을 넘긴다.

import styled from 'styled-components'

const backgroundColor = {
  primary: '#677987',
  secondary: '#B4B6AC'
}

interface ButtonProps {
  children: string
  readonly variant: 'primary' | 'secondary'
}

const StyledButton = styled.button<ButtonProps>`
  width: 100%;
  height: 50px;
  background-color: ${(props) => backgroundColor[props.variant]};
  text-align: center;
`

const Button = ({ children, variant }: ButtonProps) => {
  return (
    <StyledButton variant={variant}>{children}</StyledButton>
  )
}

export default Button

 


최종 코드

  • src/components/Button.tsx
import styled from 'styled-components'

const backgroundColor = {
  primary: '#677987',
  secondary: '#B4B6AC'
}

const textColor = {
  primary: '#FCFCFC',
  secondary: '#FCFCFC'
}

interface ButtonProps {
  children: string
  readonly variant: 'primary' | 'secondary'
}

const StyledButton = styled.button<ButtonProps>`
  width: 100%;
  height: 50px;
  border-radius: 5px;
  background-color: ${(props) => backgroundColor[props.variant]};
  color: ${(props) => textColor[props.variant]};
  font-size: 16px;
  text-align: center;
  transition: all 0.3s;
  
  &:hover {
    opacity: 0.8;
  }
`

const Button = ({ children, variant }: ButtonProps) => {
  return (
    <StyledButton variant={variant}>{children}</StyledButton>
  )
}

export default Button

 

  • src/App.tsx

Button 컴포넌트를 import 한다.

props로 넘길 값들(children, variant)을 작성한다.

 

Title 컴포넌트도 같은 방식으로 작성됐다.

import Title from '../../components/base/Title'
import Button from '../../components/form/Button'
import './index.scss'

const App = () => {
  return (
    <>
      <div className="board-create">
        <Title children="Write content"/>
        
        <Button children="Confirm" variant="primary"/>
        <Button children="Cancel" variant="secondary"/>
      </div>
    </>
  )
}

export default App

결과화면

 


1. react-router-dom 설치하기

react-router-dom 패키지를 설치한다.

설치가 완료되면 packae.json 파일에서 확인할 수 있다.

yarn add react-router-dom
또는
npm install react-router-dom

 

2. BrowerRouter 컴포넌트를 최상위 태그에 감싸기

  • src/index.tsx

App 컴포넌트 위에 BrowerRouter 컴포넌트를 감싼다.

import React from 'react'
import ReactDOM from 'react-dom/client'
import { BrowserRouter } from 'react-router-dom'
import App from './App'

const root = ReactDOM.createRoot (
  document.getElementById('root') as HTMLElement
)

root.render(
  <React.StrictMode>
    <BrowserRouter>
      <App/>
    </BrowserRouter>
  </React.StrictMode>
)

 

3. path를 설정하기

  • src/App.tsx

path와 그에 해당하는 element 컴포넌트를 작성한다.

import React from 'react'
import { Routes, Route } from 'react-router-dom'
import Layout from './components/layouts/Layout'
import MainPage from './pages/main'
import BoardPage from './pages/board'
import './assets/scss/common.scss'

const App = () => {
  return (
    <>
      {
        <Layout>
          <Routes>
            <Route path="/" element={<MainPage/>}/>
            <Route path="/board" element={<BoardPage/>}/>
          </Routes>
        </Layout>
      }
    </>
  )
}

export default App

결과 화면

 


1. node-sass 설치하기

node-sass 패키지를 설치한다.

설치가 완료되면 packae.json 파일에서 확인할 수 있다.

yarn add node-sass
또는
npm install node-sass

 

2. SCSS 작성하기

  • src/assets/scss/components/header.scss
header {
  width: 100%;

  .inner {
    display: flex;
    justify-content: space-between;
    align-items: center;
    height: 80px;

    h1 {
      a {
        font-family: 'Cherry Bomb One', cursive;
      }
    }

    nav {
      ul {
        display: flex;
        justify-content: space-between;
        align-items: center;

        li {
          padding-left: 20px;
        }
      }
    }
  }
}

 

  • src/components/layouts/Heaser.tsx

작성한 scss 파일을 상단에 import 시킨다.

import '../../assets/scss/components/header.scss'

const Header = () => {
  return (
    <>
      <header>
        <div className="inner">
          <h1><a href="/">BOB</a></h1>

          <nav>
            <ul>
              <li><a href="/">menu01</a></li>
              <li><a href="/">menu02</a></li>
              <li><a href="/">menu03</a></li>
            </ul>
          </nav>
        </div>
      </header>
    </>
  )
}

export default Header

 

  • src/assets/scss/common.scss

reset.scss, font.scss 등 공통적으로 필요한 scss 파일을 페이지마다 import 하면 번거롭고, 코드도 길어지기 때문에 common.scss를 만들어 한 개의 scss 파일에 작성되도록 한다.

// base
@import './base/reset';
@import './base/font';

// layout
@import './layout/layout';

 

  • src/App.tsx

최종적으로 common.scss를 App 컴포넌트에 import 한다.

import React from 'react'
import Layout from './components/layouts/Layout'
import './assets/scss/common.scss'

const App = () => {
  return (
    <Layout>
      <h1>내용을 작성해 주세요.</h1>
    </Layout>
  )
}

export default App

결과 화면

 


1. 레이아웃 컴포넌트 만들기

  • src/components/layouts/Header.tsx
const Header = () => {
  return (
    <>
      <header>헤더 컴포넌트입니다.</header>
    </>
  )
}

export default Header

 

  • src/components/layouts/Footer.tsx
const Footer = () => {
  return (
    <>
      <footer>푸터 컴포넌트입니다.</footer>
    </>
  )
}

export default Footer

 

  • src/components/layouts/Layout.tsx

헤더와 푸터는 페이지마다 공통으로 보여지는 영역이기 때문에 import 해서 사용하면 되지만,

메인의 컨텐츠 영역은 페이지마다 내용이 바뀌기 때문에 props로 사용한다.

import Header from './Header'
import Footer from './Footer'

const Layout = (props: {children: React.ReactNode}) => {
  return (
    <>
      <Header/>

      <main>{props.children}</main>

      <Footer/>
    </>
  )
}

export default Layout

 

2. index.tsx, App.tsx 수정하기

  • src/index.tsx

불필요한 소스를 삭제한다.

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import './index.css'

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
)

root.render(
  <React.StrictMode>
    <App/>
  </React.StrictMode>
)

 

  • src/App.tsx

레이아웃 컴포넌트를 import 한다.

<h1>의 내용이 <Layout> 컴포넌트의 <main> 태그 안에 들어가게 된다.

<Layout> 컴포넌트로 감싸 여러 페이지들을 만들면 된다.

import React from 'react'
import Layout from './components/layouts/Layout'
import './App.css'

const App = () => {
  return (
    <Layout>
      <h1>내용을 작성해 주세요.</h1>
    </Layout>
  )
}

export default App

1. Node.js 설치하기

https://nodejs.org/ko

Node.js는 Chrome V8 JavaScript 엔진으로 빌드된 JavaScript 런타임이다.

 

위의 정의는 노드를 통해서 자바스크립트 애플리케이션을 실행할 수 있다는 의미이다.

타입스크립트는 자바스크립트에 타입을 추가한 언어이기 때문에 타입스크립트를 사용하기 위해서는 Node.js를 먼저 설치해야 한다.

 

1-1. Node.js 버전 확인하기

node --version
또는
node -v

 

 

2. typescript, ts-node 글로벌 설치하기

typescript와 ts-node를 글로벌로 설치하면 리액트 프로젝트가 아니더라도 타입스크립트를 사용할 수 있기 때문에 따로 설치하는 것이 좋다.

또한 글로벌이기 때문에 1번만 설치하면 추가 설치 없이 바로 사용할 수 있다.

yarn add -g typescript ts-node
또는
npm install -g typescript ts-node

 

2-1. typescript, ts-node 버전 확인하기

tsc --version 또는 tsc -v

ts-node --version 또는 ts-node -v

 

 

3. 프로젝트 만들기

yarn create react-app 폴더명 --template typescript
또는
npx create-react-app 폴더명 --template typescript

 

tsx 파일이 생성됐는지 확인한다.

 

 

4. 불필요한 파일 삭제하기

src 폴더에서 App.css, App.tsx, index.css, index.tsx 파일만 남기고 전부 삭제한다.

 

5. 로컬호스트 연결 확인하기

http://localhost:3000/

터미널에서 yarn start 명령어를 실행해 로컬호스트에 접속이 잘 되는지 확인한다.

아래와 같은 화면이 나온다면 정상적으로 세팅이 완료된 것이다.

 

04-1 함수 선언문

자바스크립트에서 함수는 function 키워드로 만든 함수=> 기호로 만든 화살표 함수 2가지가 있다.

 

타입스크립트 함수 선언문은 매개변수와 함수 반환값(return)에 타입 주석을 붙이는 형태이다.

함수 선언문에서도 매개변수와 반환값에 타입 주석을 생략할 수 있지만, 타입이 생략되어 있으면 함수의 구현 의도를 알기 어렵기 때문에 바람직하지 않다.

 

function addNumber(~~)의 괄호 안의 값을 parameter, 매개변수 라고 하고,

콘솔의 addNumber(~~)의 괄호 안의 값을 argument, 인수, 인자 라고 한다.

 

  • function 키워드로 만든 함수
function addNumber(num1: number, num2: number): number {
  return num1 + num2
}

console.log(addNumber(1, 2)) // 3

 

  • => 기호로 만든 화살표 함수
const addString = (str1: string, str2: string): string => str1 + str2

console.log(addString('가나다', '라마바')) // 가나다라마바

 

void 타입

void 타입은 값을 반환하지 않는 함수이다.

function printMe(name: string, age: number): void {
  console.log(`name: ${name}, age: ${age}`)
}

printMe('HeeJae', 29) // name: HeeJae, age: 29

 

type 키워드로 타입 별칭 만들기

타입 별칭(type alias)의 type 키워드는 기존에 존재하는 타입을 단순히 이름만 바꿔서 사용할 수 있게 해 준다.

함수를 선언할 때, 타입 별칭을 사용하면 매개변수의 개수, 타입, 반환 타입이 다른 함수를 선언하는 것을 미리 방지할 수 있다.

type stringNumberFunc = (arg1: string, arg2: number) => void

let f: stringNumberFunc = function(a: string, b: number): void {
  console.log(a, b)
}

f('Heejae', 29) // { name: 'Heejae', age: 29 }
f('Heejae') // Expected 2 arguments, but got 1.
f() // Expected 2 arguments, but got 0.

 

type vs interface

타입 별칭과 인터페이스의 가장 큰 차이점은 확장 가능, 불가능의 여부이다.

인터페이스는 확장이 가능하지만, 타입별칭은 확장이 불가능하기 때문에 타입 별칭보다는 인터페이스를 사용하는 것을 추천한다.

 

undefined 관련 주의 사항

타입스크립트에서 undefined은 타입이면서 값이기 때문에 값을 변경할 수가 없다.

초기화를 안 했거나 값이 없을 경우, undefined으로 나오는데 오류를 방지하기 위해서 매개변수 값이 undefined인지 판별하는 코드를 작성해야 한다.

 

필수 속성은 값이 있어야 하기 때문에 undefined를 할당하면 오류가 발생하고,

선택속 성은 값이 없어도 되기 때문에 undefined를 판별하는 코드가 있으면 오류가 발생하지 않는다.

 

FIXME: getName(undefined!)에서 !가 없으면 오류, Argument of type 'undefined' is not assignable to parameter of type 'INameable'.

// 필수속성 undefined
interface INameable {
  name: string
}

function getName(userName: INameable) {
  return userName !== undefined ? userName.name : 'unknown name'
}

console.log(getName(undefined!)) // unknown name
console.log(getName({name: 'Heejae'})) // Heejae
// 선택속성 undefined
interface IAgeable {
  age?: number
}

function getAge(userAge: IAgeable) {
  return userAge !== undefined && userAge.age ? userAge.age : 'unknow age'
}

console.log(getAge(undefined!)) // unknow age
console.log(getAge({age: 29})) // 29

 

선택적 매개변수

인터페이스의 선택 속성처럼 함수의 매개변수에도 물음표(?)를 붙여 선택적 매개변수를 만들 수 있다.

function userList(name: string, age?: number) {
  console.log(`name: ${name}, age: ${age}`)
}

userList('Heejae', 29) // name: Heejae, age: 29
userList('Heejae') // name: Heejae, age: undefined

 


04-2 함수 표현식

타입스크립트와 자바스크립트는 객체지향 언어와 함수형 언어의 특징을 모두 가지고 있다.

 

함수 표현식을 선언할 때는 let보다는 const 키워드로 선언하는 것이 바람직하다.

let 변수는 값이 변할 수 있고, const 변수는 값이 절대로 변하지 않기 때문이다. 

 

  • 변수 선언문 형태의 함수
const add = new Function('a', 'b', 'return a + b')

console.log(add(1, 2)) // 3

 

  • 함수 표현식 형태의 함수
const add = function(a: number, b: number) { return a + b }

console.log(add(1, 2)) // 3

 

표현식: 리터럴, 연산자, 변수, 함수 등 복합적으로 구성된 코드 형태

함수 표현식(=익명함수): 함수 이름을 제외한 함수 코드

함수 호출: 함수 표현식의 몸통 부분을 실행

 

일등 함수

일등 함수란, 변수와 함수를 구분하지 않는다는 의미이다.

위의 코드를 보면 add 라는 변수에 함수를 할당했기 때문에 add가 변수인지 함수인지 사실상 구분할 수 없다.

 


04-3 화살표 함수와 표현식 문

C언어는 모든 문장이 반드시 세미콜론 ;으로 끝나야 하고, C언어 구문을 참조해 만든 ES5 자바스크립트 또한 모든 문장 끝에 세미콜론이 있어야 한다. 반면에 ESNext 자바스크립트와 타입스크립트에서는 세미콜론을 생략할 수 있다.

 

  • 실행문 지향 언어

실생문은 CPU에서 실행되는 코드를 의미한다. 

실행문은 CPU에서 실행만 될 뿐 결과는 알려주지 않기 때문에 return 키워드를 사용해서 실행된 결과를 알려줘야 한다.

 

ex) C언어, ES5, ESNext, 타입스크립트

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

console.log(add(1, 2)) // 3

 

  • 표현식 지향 언어

표현식은 CPU에서 실행된 결과를 return 키워드를 사용하지 않아도 알려준다.

 

ex) 스칼라, ESNext, 타입스크립트

const add = (a: number, b: number): number => a + b

console.log(add(1, 2)) // 3

 

실행문과 표현식을 동시에 지원하는 언어 = 다중 패러다임 언어= ESNext, 타입스크립트

 

복합 실행문과 return

복합 실행문은 중괄호{}를 사용해서 작성하고, 컴파일러는 여러 줄의 실행문을 한 줄의 실행문으로 인식한다.

실행문에서 return을 작성하지 않으면 'void 함수 반환 값이 사용되었습니다' 라는 오류가 발생한다.

return 키워드는 반드시 함수의 몸통에서만 사용할 수 있다.

 

복합 실행문의 유효범위는 local scope(지역 범위)이다.

function f() {
  let x = 1
  return x
}

function g() {
  let x = 2
  return x
}

console.log(f()) // 1
console.log(g()) // 2

 


04-4 일등 함수 살펴보기

콜백 함수

콜백 함수는 매개변수 형태로 동작하는 함수이다.

콜백 함수는 프레임워크의 API 구현에 매우 유용하다.

export const init = (callback: () => void): void => {
  console.log('first message')
  callback()
  console.log('second message')
}

init(() => console.log('function message'))

// first message
// function message
// second message

 

중첩 함수

함수형 언어에서 함수는 함수 표현식 안에 또 다른 함수를 작성할 수 있다.

const calc = (num1: number, num2: (result: number) => void): void => {
  let add = (a: number, b: number) => a + b

  function multiply(a: number, b: number) {return a * b}
  let result = multiply(add(1, 2), num1)

  num2(result)
}

calc(30, (result: number) => console.log(`result is ${result}`)) // result is 90

 

고차 함수와 클로저, 그리고 부분 함수

고차 함수는 또 다른 함수를 반환하는 함수이다. 함수형 언어에서 함수 표현식 또한 값이기 때문에 다른 함수를 반환할 수 있다.

고차 함수는 함수 안에 또 다른 함수가 있기 때문에 변수의 스코프가 중요한데, 함수를 return 하고, return 하는 함수가 클로서를 형성한다.

const add = (a: number): (b: number) => number => (b: number): number => a + b
const result = add(10)(20)

console.log(result) // 30

 


04-5 함수 구현 기법

매개변수 기본값 지정하기

선택적 매개변수는 항상 그 값이 undefined로 고정된다. 만약 함수 호출 시, 인수를 전달하지 않더라도 매개변수에 어떤 값을 설정하고 싶다면 기본값을 지정할 수 있고, 이를 디폴트 매개변수라고 한다.

export type Person = {name: string, age: number}

export const makePerson = (name: string, age: number = 10): Person => {
    // 단축 구문(shorthand)
    const person = {name, age}
    return person
}

console.log(makePerson('Jack')) // { name: 'Jack', age: 10 }
console.log(makePerson('Jack', 33)) // { name: 'Jack', age: 33 }

 

객체를 반환하는 화살표 함수 만들기

화살표 함수에서 객체를 반환할 때, 아래처럼 중괄호로 구현한다면 객체가 아닌 복합 실행문으로 해석하여 오류가 발생한다.

export type Person = {name: string, age: number}

// Left side of comma operator is unused and has no side effects.
export const makePerson = (name: string, age: number = 10): Person => {name, age}

소괄호로 감싸주어 객체임을 알려준다.

export type Person = {name: string, age: number}

export const makePerson = (name: string, age: number = 10): Person => ({name, age})

console.log(makePerson('Jack')) // { name: 'Jack', age: 10 }
console.log(makePerson('Jack', 33)) // { name: 'Jack', age: 33 }

 

매개변수에 비구조화 할당문 사용하기

비구조화: 구조화된 데이터의 일부만 사용하는 것

객체뿐만 아니라 함수의 매개변수도 비구조화 할당문을 적용할 수 있다.

export type Person = {name: string, age: number}

export const makePerson = ({name, age}: Person): void => {
    console.log(`My name is ${name}. My age is ${age}.`)
}

console.log(makePerson({name: 'Jack', age: 10})) // My name is Jack. My age is 10.

 

색인 키와 값으로 객체 만들기

색인 가능 타입(indexable type)은 {[key]: value} 형태를 의미하고, key와 value의 타입을 명시한다.

객체의 속성 이름을 변수로 만들 때 사용한다. (키 값을 변경할 수 있다.)

export type KeyValeuType = {
    [key: string]: string
}

const makeObject  = (key: string, value: string): KeyValeuType => (
    {[key]: value}
)

console.log(makeObject('key', 'value')) // { key: 'value' }
console.log(makeObject('NAME', 'Jack')) // { NAME: 'Jack' }

 


04-6 클래스 메서드

function 함수와 this 키워드

function 키워드로 만든 함수는 this를 사용할 수 있지만,

화살표 함수는 this를 사용할 수 없다.

 

클래스 메서드 구문

타입스크립트에서 메서드는 function으로 만든 함수 표현식을 담고 있는 속성이다.

생성자를 통해 전달된 값이 value에 설정되고, method가 호출돼서 값이 출력된다.

export class B {
    constructor(public value: number = 1) {}
    method(): void {
        console.log(`value: ${this.value}`)
    }
}

let a: B = new B()
a.method() // value: 1

let b: B = new B(2)
b.method() // value: 2

 

정적 메서드

메서드 이름 앞에 static을 붙여 정적 메서드를 만들 수 있다.

export class C {
    static whoAreYou(): string {
        return 'I am class C'
    }
}

export class D {
    static whoAreYou(): string {
        return 'I am class D'
    }
}

console.log(C.whoAreYou()) // I am class C
console.log(D.whoAreYou()) // I am class D

 

메서드 체인

메서드 체인(method chain)은 객체의 메서드를 이어서 계속 호출하는 방식의 코드를 의미하고, 타입스크립트로 메서드 체인을 구현하려면 메서드가 항상 this를 반환해야 한다.

export class Caculator {
    constructor(public value: number = 0) {}

    add(value: number) {
        this.value += value
        return this
    }

    multiply(value: number) {
        this.value *= value
        return this
    }
}

let calc = new Caculator
let result = calc.add(1).add(2).multiply(3).multiply(4).value

console.log(result) // (((0 + 1 + 2) * 3) * 4) = 36

 

 

 

참고자료

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

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

 

데이터 추가하기

insert into 테이블명 values(column1, column2, ...)

insert into user_table values(1, '이희재');

 

 

데이터 수정하기

update 테이블명 set 컬럼명 = 수정된 값 where 컬럼명 = 수정할 값;

update user_table set name = '희재' where name = '이희재';

 

 

데이터 삭제하기

delete from 테이블명 where 컬럼 = 데이터;

delete from user_table where name = '희재';

 

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

 

기본키 설정하기

alter table 테이블명 add constraint 기본키 제약조건 이름 primary key(기본키로 설정할 컬럼명);

alter table user_table add constraint pk_user_id primary key(id);

 

 

기본키 삭제하기

alter table 테이블명 drop constraint 기본키 제약조건 이름;

alter table user_table drop constraint pk_user_id;

 

 

외래키 설정하기

외래키를 설정하기 위해서는 참조할 테이블이 있어야 하기 때문에 테이블이 2개 이상이어야 한다.

 

alter table 테이블명 add constraint 외래키 제약조건 이름 foreign key(외래키로 설정할 컬럼명) references 참조할 테이블명(참조 테이블 컬럼명);

alter table user_table add constraint fk_user_id foreign key(id) references board_table(id);

 

 

외래키 삭제하기

alter table 테이블명 drop constraint 외래키 제약조건 이름;

alter table user_table drop constraint fk_user_id;

 

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

 

컬럼 생성

alter table 테이블명 add 컬럼명 데이터타입;

alter table user_table add id integer;

 


컬럼 수정

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

alter table user_table rename column age to phone_number;

 

 

컬럼 삭제

alter table 테이블명 drop column 컬럼명;

alter table user_table drop column phone_number;

 

+ Recent posts