업데이트:

카테고리:

/

태그:

TypeScript

JavaScript 가 서버 측에서 사용하기가 힘들고 컴파일의 기능을 수용하지 못해서 Typescript가 대신하게 되었다.

  • JavaScript + 타입시스템
  • JavaScript 코드를 더욱 단순화 하고 쉽게 디버그
  • 개발자들이 버그 수정에 대한 고통을 줄여주기 위해서

데이터 미리넣어두기

  • md 파일을 이용해서 데이터를 미리 랜더할 수 있게 한다.
  • 원래는 서버에서 요청하여 데이터를 받지만 pre-render는 서버의 요청을 못보내니 미리 데이터를 넣어준다.
  • 포스트를 클릭할 때 상세페이지를 보여주기 위해 임의로 데이터를 넣어준다.
  • posts 폴더를 생성하여 md 파일로 데이터를 넣어준다.

md 파일에서 데이터 추출하기

상위 폴더에 lib 폴더 생성 및 post.ts 파일을 생성한다

설치 : npm install --save gray-matter

⇒ 데이터를 변경하는데 사용하는 모듈, md파일을 객체형태로

import fs from 'fs'
import path from 'path'
import matter from 'gray-matter'

// path 미리 설정
const postsDirectory = path.join(process.cwd(), 'posts');

export function getSortedPostsData() {
    // post 파일 이름 잡아주기
    const fileNames = fs.readdirSync(postsDirectory);
    // ['pre-rendering.md', 'ssg-ssr.md']
    
    const allPostsData = fileNames.map(fileName => {
        // md가 나오면 없애버린다
        const id = fileName.replace(/\.md$/, "");

        const fullPath = path.join(postsDirectory, fileName);
        // 해당 파일을 불러들인다.
        const fileContents = fs.readFileSync(fullPath, 'utf-8');
        
        // 파일의 콘텐츠를 객체로 변환
        const matterResult = matter(fileContents);

        return {
            id,
            ...(matterResult.data as {date: string; title:string})
        }
    })

    // 정렬
    return allPostsData.sort((a, b) => {
        if (a.date < b.date) {
            return 1
        } else {
            return -1
        }
    })

}

데이터 사용하기

import type { GetStaticProps, NextPage } from 'next'
import Head from 'next/head'
import { getSortedPostsData } from '../lib/posts'
import homeStyles from '../styles/Home.module.css'

// props로 데이터의 형식을 다 지정해준다.
const Home = ({ allPostsData }: {
  allPostsData: {
    date: string
    title: string
    id: string
  }[]
}) => {
  return (
    <div className={homeStyles.container}>
      <Head>
        <title>Cheon Sohee</title>
      </Head>
      <section className={homeStyles.headingMd}>
        <p>[Cheon Sohee Introdution]</p>
        <p>
          [This is a website]
        </p>
      </section>
      <section className={`${homeStyles.headingMd} ${homeStyles.padding1px}`}>
        <h2 className={homeStyles.hedingLg}>Blog</h2>
        <ul className={homeStyles.list}>
					// map으로 각각의 항목을 지정해준다.
          {allPostsData.map(({ id, date, title }) => (
            <li className={homeStyles.listItem} key={id}>
              <a>{title}</a>
              <br />
              <small className={homeStyles.lightText}>
                {date}
              </small>
            </li>
          ))}
        </ul>
      </section>
    </div>
  )
}

export default Home
// 위에서 작성했던 함수를 가져와 리턴값을 props로 넣어준다.
// const getStaticProps: GetStaticProps는 getStaticProps의 타입을 정해주는 것
export const getStaticProps: GetStaticProps = async () => {
  const allPostsData = getSortedPostsData();
  return {
    props: {
      allPostsData
    }
  }
}

TypeScript의 타입

"apple"

⇒ 문자열
⇒ value인데 문자열이 가지는 프로퍼티는, 메소드를 가지고 있는 value 이다.

Property : apple.length는 문자열의 속성인 길이를 제공, 문자열 자체에는 아무것도 하지 않음
Method : apple.lowerCase()는 문자열을 소문자로 반환, 문자열에 작업을 한 후 반환을 한다.

추가제공 타입

  1. Any
    • 잘 알지 못하는 타입을 표현해야 할 때 , 타입 검사를 통과하기 위해 사용한다.
    • 최대한 사용하지 않는 것이 좋다.
    • nolmplictAny 를 사용하면 Any 타입에 오류가 발생한다.
  2. Union
    • 변수, 함수 매개변수에 대해 둘 이상의 데이터 유형을 사용할 수 있다.
     let code: (string | number);
     code = 123; // OK
     code = "ABc" // OK
     code = false // Nope
    
  3. Tuple
    • 배열보다 특수한 형태로 사용한다.
    • 지정된 형식에 다라 아이템 순서를 설정, 추가되는 아이템 또한 명시된 타입만 가능하다.
  4. Enum
    • 열거형을 의미
    • 값들의 집합을 명명하고 이를 사용하도록한다.
    • 기억하기 어려운 숫자대신 이름으로 명명하기 위해 사용된다.
    • 선언 이후에는 변경할 수 없고, 속성값으로 문자열이나 숫자만 허용한다.
     enum PrintMedia {
     	Newspaper,
     	Newsletter,
     	Magazine,
     	Book,
     }
        
     let mediaType: number = PrintMedia.Book // 3번째 집합을 불러온다.
        
     enum PrintMedia {
     	Newspaper = 1,
     	Newsletter = 2,
     	Magazine = 40,
     	Book = 23,
     }
        
     let mediaType: number = PrintMedia[40] // Magzine
    
  5. void
    • 데이터가 없는 경우 사용된다.
    • 함수에 리턴이 없을 때 void를 지정할 수 있다.
  6. Never
    • 절대 발생하지 않을 값을 나타냄
    • 함수의 리턴값으로 사용된다. 항상 오류를 리턴하거나 리턴값을 절대로 내보지 않는다.
    • 무한루프를 돌릴때도 사용한다.

💡 voidNever 의 차이는?

  • void : 유형이 값이기 때문에 undefinednull 을 가질 수 있다.
  • Never : 어떠한 값도 지정할 수 없다.

type annotation

개발자가 타입을 직접 말해 주는것

type inference

타입스크립트가 알아서 타입을 추론하는 것

annotation 꼭 해줘야 되는 경우

  1. any 타입을 리턴할 때
    • json.parse의 경우 텍스트 데이터를 json 으로 파싱해준다. 개발자는 어떤 타입으로 리턴 될지 알고 있지만 타입스크립트는 전혀 모르기때문에 annotation을 해주어야 한다.
  2. 변수 선언을 먼저하고 나중에 초기화 할 때
    • 선언을 먼저하면 타입스크립트가 추론할 수 없음
  3. 변수에 대입될 값이 일정치 못한 경우
    • 여러타입이 지정될 때에는 | or statement 로 여러 타입을 애노테이션 해준다.

type assetion

추론한 타입을 우리가 원하는 대로 바꿀 수 있다. 이때 type assetion 이라는 매커니즘이 사용된다.

프로그래머가 타입에 대해 잘 알고 있을 때 사용한다.

  1. as Foo
  2. <Foo>

이 두가지 방식으로 사용하지만 <Foo>는 react의 문법과 겹치므로 as Foo 를 사용한다.

file System 기반의 라우팅

리액트에서는 react-router를 사용하지만, Next.js에서는 파일이 페이지 디렉토리에 추가되면 자동으로 경로를 사용할 수 있다.

Link 함수를 이용해서 페이지 이동을 구현한다.

// 어떤 파일로 이동할 것인지 링크를 걸어둔다.
<Link href={`/posts/${id}`}>
  <a>{title}</a>
</Link>

설치: npm install remark remark-html --save

// 특정 하나의 데이터만 가져오기
export function getPostData(id: string) {
    const fullPath = path.join(postsDirectory, `${id}.md`);
    // 파일읽기
    const fileContents = fs.readFileSync(fullPath, 'utf-8');

    const matterResult = matter(fileContents);

    // 데이터 변환작업
		// md파일을 html 태그로 변환해준다.
    const processedContent = await remark().use(remarkHtml).process(matterResult.content)
    const contentHtml = processedContent.toString();
}

Untitled

⇒ 변환작업을 거치고 나면 해당 형태로 데이터가 바뀌게 된다.

// 경로 가져오기
export const getStaticPaths: GetStaticPaths = async () => {
  const paths = getAllPostIds();
  // [{params: {id:...}}, {params: {id:...}}]
  return {
    paths,
    // 데이터가 없으면 404 에러 발생
    fallback: false
  }
}

// 경로 기반으로 데이터 가져오기
export const getStaticProps: GetStaticProps = async ({params}) => {
  // id
  const postData = await getPostData(params.id as string)

  return {
    props: {
      postData
    }
  }
}