이번에는 Git - AWS ECS 서비스를 연동해보고자 한다.
(AWS 계정이 필요하다.)


AWS - IAM 설정

AWS - IAM 페이지에서 사용자 생성을 진행한다.

그 다음 액세스 키를 발급한다.

AWS CLI 이용할 예정이기 때문에 아래와 같이 선택한다.

AccessKey를 발급 받으면

[AccessKey, SecretAccessKey]

두 개가 발급되는데, 어디다가 저장해두자.


Git repository - AccessKey 등록

이제 발급 받은 AccessKey와 SecretAccessKey를 Next 프로젝트 레포지토리에 등록해야 한다.

AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY 를 각각 등록해준다.


Next.js 프로젝트 workflow 수정

방금 추가한 Access, Secret 키를 프로젝트 내 github / workflow 파일에 추가해준다.
그래야 해당 키를 가지고 AWS와 연동할 수 있게 된다.

변경사항을 push하여 Git Action을 확인해보도록 하자.

현재는 AccessKey를 설정하는 단계에서 그쳤지만, workflow에 다른 AWS 서비스를 사용할 수 있도록 steps를 추가한다면, 해당 키가 잘 설정되어야만 오류 없이 배포가 된다.


IAM 권한 설정

이제 AWS로 프로젝트를 배포할 모든 서버에 동일한 환경을 구축하기 위해 Docker Image 생성 및 해당 Image의 Repository 역할을 하는 ECR 서비스를 사용할 예정이다.

그러나 해당 작업에 앞서 ECR과 같은 AWS 서비스를 바로 사용할 수는 없다.
우리는 앞선 과정에서 IAM 그룹 or 사용자를 생성하였고 각 서비스에 맞는 권한을 설정해주어야 한다.
필자는 그룹 단위로 권한을 부여하고, 해당 그룹에 속한 사용자들에게 동일한 권한을 부여해주는 방식으로 진행하려고 한다.

IAM 그룹에 권한 설정

그룹에 권한을 추가하려면 [사용자 그룹 - 권한 추가 - 정책 연결]
사용자별 권한을 추가하려면 [사용자 - 권한 추가 - 정책 연결]을 통해 권한을 추가할 수 있다.

필자는 아래와 같이 추가하였다.
아래 빨간 박스를 제외한 다른 권한들은 AWS에 이미 존재하는 권한들이고
빨간 박스는 별도로 추가해주었다.

ecs-cicd-demo-role 추가 방법 (이름은 마음대로 지어주세요)
[사용자 or 사용자 그룹 - 권한 추가 - 인라인 정책 생성 - 정책편집기 JSON] 선택 후 아래 JSON 붙여넣기

{
    "Version": "2012-10-17",
    "Statement":
    [
        {
            "Effect": "Allow",
            "Action":
            [
                "iam:PassRole"
            ],
            "Resource": "arn:aws:iam::*:role/ecsTaskExecutionRole"
        }
    ]
}

그 다음 정책의 이름을 설정해주면 끝이다.

결론적으로 추가한 정책들은 아래와 같다.

//ECR에 대한 모든 권한
AmazonEC2ContainerRegistryFullAccess

//ECS에 대한 모든 권한
AmazonECS_FullAccess

//ECR Public에 대한 모든 권한
AmazonElasticContainerRegistryPublicFullAccess

//S3에 대한 모든 권한
AmazonS3FullAccess

//ECS 클러스터로 애플리케이션 배포를 관리할 때 사용되는 권한
AWSCodeDeployRoleForECS

//AWSCodeDeployRoleForECS와 동일한 목적이지만 최소한의 권한만 부여
AWSCodeDeployRoleForECSLimited

//ECR 컨테이너 빌드를 위한 EC2 프로필을 나타내는 역할
EC2InstanceProfileForImageBuilderECRContainerBuilds

//ELB에 대한 모든 권한
ElasticLoadBalancingFullAccess

//ECS에서 실행 중인 Task에 대한 IAM 역할. 컨테이너 실행 및 관련 작업을 수행 - 별도로 직접 추가 필요
ecs-cicd-demo-role

ECR 관련 workflow 추가 및 배포 확인

이제 github workflow 하단에 ECR 로그인 관련 코드를 추가해준다.

name: Staging # 워크플로 Action의 이름
on:
  push:
    branches: [main] #github main 브런치에 푸시 발생 시 실행 ( main 외 다른브런치 이용시 이름변경)
jobs:
  staging: #staging이라는 작업
    name: deploy to staging # 작업의 이름
    runs-on: ubuntu-latest # 실행될 작업환경을 말함.
    steps:
      - name: Checkout
        uses: actions/checkout@v3 #체크아웃 받기
      - name: HelloWorld
        uses: actions/hello-world-javascript-action@v1 # 헬로월드 찍어보기
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} # Github Secret 이름
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} # Github Secret 이름
          aws-region: ap-northeast-1 # AWS 리전 선택
        #AWS - ECR
      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v1

추가 성공!


정리

  1. AWS의 서비스들을 사용하기 위해서는 IAM 사용자 혹은 사용자 그룹을 생성한다.
  2. IAM 사용자 혹은 사용자 그룹에 AccessKey, SecretAccessKey를 발급 받아 github repository에 등록 및 workflow에 설정한다.
  3. 필요한 AWS 서비스를 사용하기 위해 그에 맞는 권한을 설정한다.
  4. AWS ECR 관련 workflow를 설정한다.

다음 포스팅에서는 본격적으로 ECR, ECS 서비스를 사용해볼 예정이다.
처음이라면 이 모든 과정이 쉽지 않겠지만 천천히 공부하다보면 언젠가는 될 것이라 믿는다.

Docker란 무엇인가?

Docker는 컨테이너화 기술을 사용하여 애플리케이션을 개발, 배포 및 실행하는 데 사용되는 오픈 소스 플랫폼 및 생태계이다. Docker를 사용하면 애플리케이션과 그 종속성을 컨테이너라고 하는 경량 가상 환경에 패키징할 수 있으며, 이러한 컨테이너는 호스트 시스템과 독립적으로 실행다.

Docker의 주요 특징과 개념

  1. 컨테이너: Docker 컨테이너는 애플리케이션, 라이브러리 및 종속성을 패키징하는 데 사용되는 경량 환경이다. 각 컨테이너는 격리된 환경에서 실행되며 호스트 시스템과 공유 커널을 사용한다.

  2. 이미지: Docker 컨테이너를 생성하기 위한 템플릿으로서, 이미지는 애플리케이션과 해당 종속성을 포함한다. 이미지는 컨테이너를 여러 환경에서 생성하고 실행하는 데 사용된다.

  3. 도커 엔진: Docker 엔진은 컨테이너를 관리하고 실행하는 핵심 구성 요소이다. Docker 엔진은 Docker 데몬 및 Docker CLI(Command Line Interface)를 포함하며 컨테이너 작업을 제어한다.

  4. 이식성: Docker 컨테이너는 호스트 환경과 독립적이므로 어디에서나 실행할 수 있다. 이는 개발 환경에서 프로덕션 환경으로 애플리케이션을 이동할 때 매우 유용하다.

  5. 자동화: Docker를 사용하면 애플리케이션의 빌드, 배포 및 스케일링을 자동화할 수 있다. Docker Compose 및 Kubernetes와 같은 도구를 통해 애플리케이션 스택을 정의하고 관리하는 데 도움을 준다.

  6. 확장성: Docker 컨테이너는 가벼우면서 확장 가능하므로 대규모 애플리케이션 배포 및 관리에 적합하다.

Docker는 개발자, 운영팀 및 DevOps 엔지니어 등에게 애플리케이션 배포 및 관리를 단순화하고 효율적으로 관리할 수 있는 강력한 도구이다. 컨테이너화 기술은 애플리케이션의 일관성, 이식성 및 확장성을 향상시키는 데 도움이 된다.


Docker 사용법

Docker의 동작 순서

DockerFile : Container를 어떻게 만들어야 하는지에 대한 설명서 느낌이다.

Image : Docker 파일을 가지고 만들 수 있다.

Container : Image를 고립된 환경(개별적인 FileSystem 안에서) 에서 실행할 수 있는 단위


주요 인스트럭션

FROM : base image를 지정. 주로 OS 이미지나 런타임 이미지를 지정

RUN : 이미지를 빌드할 때 사용되는 커맨드를 설정할 때 사용

ADD : 이미지에 호스트의 파일이나 폴더를 추가하기 위해 사용.
만약 이미지에 복사하려는 디렉토리가 존재하지 않으면 docker가 자동으로 생성

COPY : 호스트 환경의 파일이나 폴더를 이미지 안으로 복사하기 위해 사용
'ADD'와 동일하게 동작하지만 가장 확실한 차이점은
URL을 지정하거나, 압축파일을 자동으로 풀지 않음

EXPOSE : 이미지가 통신에 사용할 포트를 지정할 때 사용

ENV : 환경 변수를 지정할 때 사용
여기서 설정한 변수는 $name, ${name}의 형태로 사용 가능
추가로 아래와 같은 문법으로 사용할 수 있음
- ${name:-else} : name이 정의가 안되어 있다면 'else'가 사용됨

CMD : 도커 컨테이너가 실행될 때 실행할 커맨드를 지정
'RUN'과 유사하지만 CMD는 도커 이미지를 빌드할 때 실행되는 것이 아니라
컨테이너를 시작할 때 실행된다는 것이 다름

ENTRYPOINT : 도커 이미지가 실행될 때 사용되는 기본 커맨드를 지정 (강제)

WORKDIR : RUN, CMD, ENTRYPOINT 등을 사용한 커맨드를 실행하는 디렉토리를 지정
-w 옵션으로 오버라이딩 할 수 있음

VOLUME : 퍼시스턴스 데이터를 저장할 경로를 지정할 때 사용
호스트의 디렉토리를 도커 컨테이너에 연결
주로 휘발성으로 사용되면 안되는 데이터를 저장할 때 사용

DockerFile 생성

FROM node:18-alpine

WORKDIR / app

COPY package.json package-lock.json ./

RUN npm ci

COPY routes/index.js .

ENTRYPOINT [ "node", "index.js" ]

DockerFile 설명

코드 설명 하기 전에 알아둘 점
도커는 레이어 시스템을 차용했기 때문에 빈번하게 발생하는 작업은 아래쪽에 작성해준다.
그러면 가장 빈번한 작업은 레이어의 제일 위쪽에 위치하게 된다.
이미지를 만들 때 변경된 사항부터 이후 단계들에 대해서만 새로 만들고 변경이 없는 레이어는 캐시된 데이터를 그대로 사용한다.
이게 무슨 말인가 하면, 위 코드 맨 첫 줄부터 Layer0 ~ Layer5가 되며, index.js 파일이 변경 되면 이미지를 만들 때 Layer4, 5만 새로 작성한다는 의미이다.
따라서 Docker File을 잘 작성하면 이미지 생성시 불필요한 작업을 줄여 성능을 개선시킬 수 있다.

  • FROM node:18-alpine
    => 베이스 이미지를 설정한다. node의 경우는 전용 베이스 이미지를 지원하기 때문에 node:[version] 형식으로 설정한다.

  • WORKDIR / app
    => root 경로에 있는 app이라는 디렉토리에 우리 프로젝트에 관련된 모든 파일들을 copy해오겠다라는 뜻.

  • COPY package.json package-lock.json ./
    => 프로젝트에 필요한 모듈들에 대한 정보가 들어 있는 package 파일을 copy한다.

  • RUN npm install or ci
    => package.json / package-lock.json에 설정된 모듈을 설치한다.
    => npm install을 사용할 경우 특정 라이브러리에서 ‘버전 몇 이상은 다 괜찮다’ 라고 명시를 해주면 최신 버전이 나올 때마다 해당 버전을 새로 설치한다. 그러나 ci를 사용하면 package-lock.json에 설정된 버전 그대로 설치한다.

  • COPY index.js
    => index.js 파일을 copy한다.

  • ENTRYPOINT [”node”, “index.js”]
    => node를 실행하고, index.js를 실행한다.

Docker File로 Image 및 Container 만들기

Docker build

$docker build -f Dokcerfile -t today-lunch .

위 명령어를 해석하면 아래와 같다.
docker build - 말 그대로 도커를 빌드한다는 뜻.
-f Dockerfile - Dockerfile이라는 파일명을 가진 DockerFile을 명시한다.

만약 도커파일명이 다르다면 -f [도커파일명]을 작성한다.
-t image name - 도커이미지의 이름을 부여
위 명령어가 제대로 실행되면 도커 이미지가 생성된다.

Docker Image 생성

docker images

이미지에 대한 정보를 확인할 수 있다.

Docker Container 실행

docker run -d -p 8080:8080 today-lunch

-d (detached) - 컨테이너를 백그라운드에서 실행하겠다는 의미
-p (port 지정) - host machine의 포트 8080과 container의 포트 8080을 연결해주는 작업

컨테이너 정보 확인

docker ps

Next.js 프로젝트 생성

설치 및 실행

vscode를 열고 원하는 폴더에 접근한 후 다음 명령어를 실행한다.

$npx create-next-app

globals.css 파일에서 tailwind CSS 관련된 설정만 남겨두고 나머지는 전부 삭제한다.
page.tsx에서도 “Hello Next.js!” 라는 문구만 작성한다.

$npm run dev

위 명령어를 실행해서 정상적으로 웹 페이지가 열리는지 확인한다.


Github workflow 작성

생성한 Next.js 프로젝트 최상단에 다음과 같은 폴더 및 파일을 추가한다.

  1. .github
  2. .github/workflows
  3. .github/workflows/workflow.yml

이후 workflow.yml 파일에 다음과 같은 내용을 추가한다.

name: Staging # 워크플로 Action의 이름
on:
  push:
    branches: [main] #github main 브런치에 푸시 발생 시 실행 ( main 외 다른브런치 이용시 이름변경)
jobs:
  staging: #staging이라는 작업
    name: deploy to staging # 작업의 이름
    runs-on: ubuntu-latest # 실행될 작업환경을 말함.
    steps:
      - name: Checkout
        uses: actions/checkout@v3 #체크아웃 받기
      - name: HelloWorld
        uses: actions/hello-world-javascript-action@v1 # 헬로월드 찍어보기

git에 push하여 Action이 잘 동작하는지 확인한다.

커밋 메세지가 이상하지만 Action은 정상적으로 동작한다!


다음 포스팅에서는 AWS 연동을 진행할 예정이다.
AWS에서 제공하는 다양한 서비스에 대해 가능하면 최대한 짚어볼 예정이다.

Next.js 샘플 프로젝트를 Git Workflow / Action을 통해 AWS ECS로 자동 배포하는 시스템을 구축해보려 한다.


자동 배포 sequence

아래와 같은 순서를 거쳐 최종적으로 AWS ECS에 프로젝트가 자동 배포되는 과정을 경험해보고자 한다.

  1. Next.js 프로젝트 생성 및 배포
  2. Git workflow 작성
  3. AWS ECS 서비스에 연동

uml diagram

선행되어야 할 학습 내용

  • Next.js 설치 및 프로젝트 생성
  • Git / Github - workflow 설정과 Git Action
  • AWS
    • Route53 - 도메인 호스팅
    • EC2 - 클라우드 인스턴스
    • Load Balancer - 인스턴스에 접근할 트래픽 분산 처리
    • ECR - Docker Image의 저장소
    • ECS - ECR에 저장된 Docker Image를 기반으로 인스턴스를 생성하는 서비스
  • Docker - 배포한 시스템이 모든 환경에서 동일할 수 있도록 컨테이너라는 청사진 역할을 하는 시스템 구축 환경 제공

Route53이란?

Route53은 가용성과 확장성이 뛰어난 DNS 웹 서비스이다.
세 가지 주요 기능이 있는데,

  1. 도메인 등록
  2. DNS 라우팅
  3. 리소스 상태 확인
    등의 기능을 지원한다.

도메인 등록

선행으로 도메인을 가지고 있어야 한다.
AWS Route53에서 도메인들 등록할 수 있는데, 연간 12~13달러를 지불해야 한다.
필자의 경우 가비아에서 할인하는 도메인을 하나 구매했다. (훨씬 저렴함 ㅎㅎ..)
이제 내가 만든 어플리케이션을 AWS Route53을 통해 보유한 도메인에 연동할 수 있다.

DNS 라우팅

사용자가 인터넷 주소창에 도메인을 입력하거나, 그 하위 도메인을 입력했을 때 이를 라우팅해주는 기능을 지원한다.

리소스의 상태 확인

인터넷을 통해 웹 서버 같은 리소스로 자동화된 요청을 보내어 접근 및 사용이 가능하고,
정상 작동 중인지 확인한다.
리소스를 사용할 수 없게 될 때 알림을 수신하고 비정상 리소스가 아닌 다른 곳으로 인터넷 트래픽을 라우팅할 수 있다.

Next.js란?

Next.js에 대한 공식 홈페이지의 설명
세계 최대 기업에서 사용하는 Next.js를 사용하면 최신 React 기능을 확장하고 가장 빠른 빌드를 위해 강력한 Rust 기반 JavaScript 도구를 통합하여 풀 스택 웹 애플리케이션을 만들 수 있습니다.


Next.js는 Vercel이라는 회사에서 개발한 프레임워크로 React를 강력하게 만들어준다.
Next의 강점에 대해 나열하자면 아래와 같다.

Next.js의 장점

  1. 이미지 최적화 및 다양한 글꼴 지원
    • 이미지는 웹 페이지에서 엄청난 무게감을 가지기 때문에 페이지 로딩 시간에 직접적인 영향을 준다. Next에서는 Image라는 컴포넌트를 지원해 이미지에 대한 최적화를 가능하게 해준다.
    • google fonts에서 지원하는 다양한 글꼴을 Next에서 쉽게 적용할 수 있도록 지원한다.
  2. SSR(ServerSideRendering)
    • SSR : 서버에서 페이지를 그려 클라이엍느로 보낸 후 화면에 표시하는 기법
    • SSR로 인해 SEO 최적화에 효과적이다.
    • SEO : 웹 사이트의 검색 엔진 결과 페이지에서 높은 순위를 차지하도록 웹사이트를 최적화하는 프로세스이다.
    • 서버에서 정적인 페이지가 이미 렌더링 되어 클라이언트에게 전달되기 때문에 meta 태그가 미리 정의되어있고, 이 때문에 SEO에 유리하다.
  3. full-stack 개발 가능
    • 백엔드 코드 작성이 가능하다.
  4. 우수한 동적 라우팅 기능
    • page 단위로 경로가 생성되며, 동적 라우팅에 대한 별도 설정 없이 file-based 동적 라우팅을 지원한다.

이 외에도 정말 많은 기능을 지원하며, 공식 문서가 잘 되어있어 학습에 용이하다.
Next.js는 현재 유명한 기업에서 자주 사용되는 기술이며, 그 성능은 이미 검증되었다.

[Flutter] Flutter 설치방법


Flutter를 사용하기 위해 안드로이드 스튜디오를 설치하던 도중 에러가 발생했다.

Android toolchain - develop for Android devices     
✗ Unable to locate Android SDK.       
Install Android Studio from: https://developer.android.com/studio/index.html       
On first launch it will assist you in installing the Android SDK components.       
(or visit https://flutter.dev/docs/get-started/install/macos#android-setup for detailed instructions).       
If the Android SDK has been installed to a custom location, please use `flutter config --android-sdk` to update to that location.

바로

Android toolchain - develop for Android devices

라는 에러가 발생했는데,
에러 그대로 Android SDK를 사용할 수 없어서 나타나는 에러인 것 같다.
구글링한 결과 안드로이드 스튜디오에서 몇 가지 설정을 통해 해결할 수 있었다.


안드로이드 스튜디오의

SDK Manager

에 들어가면 위와 같은 페이지가 열리는데,

SDK Tools -> Hide Obsolete Packages 체크를 해제

하면

Android SDK Command-line Tools (latest)
Android SDK Tools (Obsolete)

위 두 옵션이 보인다.
설치하여 적용 후 다시 flutter doctor 명령어를 입력했더니 이번에는

Android toolchain - develop for Android devices (Android SDK version 34.0.0)     
! Some Android licenses not accepted. 
To resolve this, run: flutter doctor --android-licenses

이러한 warning이 나타났다.
허가되지 않은 안드로이드 라이센스가 있다고 하며, "

flutter doctor --android-licenses

"를 입력하라고 한다.

위 과정까지 끝내고 나서야 모든 항목에 초록불이 켜졌다.
이제 flutter를 사용할 수 있다.

사실 설치 과정이 꽤 복잡하여 정리를 해보았다.
필자는 macOS를 사용중이라 mac에 집중하여 설치 방법을 소개하려고 한다.

Flutter 설치에는 두 가지 단계가 있다.

  1. SDK 설치
    • 먼저 flutter.dev 사이트에 접속 후 Get Started 버튼을 찾아 들어가면 운영체제마다 Install을 할 수 있도록 안내되어 있다.
      => 해당 방법은 .zip 파일을 설치해 경로를 설정하는 등등의 과정이 필요하다.
    • 더 쉽게 설치하려면 homebrew를 이용하자 (mac)
    • 터미널을 실행한 후 $brew install --cask flutter
    • 별다른 에러가 없다면 잘 설치된 것이다.
  2. 시뮬레이터 설치
    • 시뮬레이터 설치가 굉장히 까다롭다.
    • IOS, Android 등 OS마다 전부 설치 방법이 다르다…
    • 그냥 공식문서 보고 잘 따라해보자. Install 페이지를 찾았다면 우측 목차에 [iOS setup / Android setup] 두 목차가 있을 것이다.
    • 필자는 iOS, Android 둘 다 셋팅해보려 한다.
    • 설치가 완료되면 $flutter doctor 명령어로 플러터를 시작할 수 있는 상태인지 확인해보자.
    • 모든 준비사항이 완료되어야 시작 가능하다.

 

'Mobile' 카테고리의 다른 글

[Flutter] Flutter의 동작 방식  (0) 2023.10.10
[Dart] Class 및 다양한 Class 속성에 대해  (0) 2023.10.10
[Dart] Dart의 Function 사용법  (0) 2023.10.10
[Dart] Dart의 Data Types  (0) 2023.10.09
[Dart] Dart의 변수 정의  (3) 2023.10.09

Google에서 개발한 오픈 소스 UI 소프트웨어 개발 키트
Dart의 프레임워크이며 모바일 앱, 웹, Linux 등 여러 플랫폼에 대해 개발이 가능하다.

동작 원리

네이티브 앱은 운영체제가 렌더링에 관여한다.
그러나

Flutter 혹은 Dart는 운영체제와 직접적으로 소통하지 않는다.

Flutter에서는 실제로 버튼을 만들어내는 등과 같은 행위는 할 수 없다.
그렇다면 어떻게 UI를 만들어내는 것일까?

위 구조를 살펴보면 Framework 하단에 C/C++로 이루어진 엔진이 존재한다.
네이티브 앱은 운영체제(IOS, AOS 등)와 직접적인 상호작용을 통해 모바일 앱 상의 화면을 그려내지만 Flutter는 엔진이 렌더링을 한다.

그렇다. Dart/Flutter로 작성된 코드를 해당 화면을 그려주는 것은 바로 엔진이다.

그리고 운영체제와 엔진 사이에서 인터페이스 역할을 해주는 것이 Embedder이다.

Embedder는 각 플랫폼에 맞게 애플리케이션이 원활하게 실행될 수 있도록 지원하며,
플러터의 크로스 플랫폼 개발을 가능하게 한다.

이제 예시로 애플리케이션 동작 Flow를 살펴보면 아래와 같다.
https://flutter-ko.dev/resources/faq#run-android

  1. 사용자가 앱을 실행함
  2. C / C++로 구현된 엔진이 각각의 운영체제에서 이해할 수 있는 표현으로 컴파일됨
  3. 엔진이 Dart 코드를 실행시킴

단점

Native 자원을 사용하지 못해 실제 운영체제로부터 화면이 그려지는게 아니라 Flutter 엔진에 의해 렌더링됨.
즉, 네이티브에서 사용 가능한 위젯을 사용할 수 없음


마무리

포스팅이 단점으로 끝나는 것 같지만, Flutter 엔진은 실로 강력한 기능을 지원해서 네이티브 위젯의 필요성을 못느낄 정도라고 한다.

또한 각 운영체제에서 앱이 실행될 수 있게 해주는 Embedder의 존재로 인해 크로스 플랫폼이 가능하다는 것은 정말 강력한 장점이라고 생각한다.

현재 시점에 제대로 된 플러터 개발을 시작하지 않아서 느낌만 가지고 말할 수는 없지만 플러터로 개발된 다양한 웹앱을 보니 정말 자연스러운 것 같다.

이제부터 세부적인 사항에 대해 차차 알아보려고 한다.

'Mobile' 카테고리의 다른 글

[Flutter] Flutter 설치방법  (0) 2023.10.12
[Dart] Class 및 다양한 Class 속성에 대해  (0) 2023.10.10
[Dart] Dart의 Function 사용법  (0) 2023.10.10
[Dart] Dart의 Data Types  (0) 2023.10.09
[Dart] Dart의 변수 정의  (3) 2023.10.09

Dart에서 클래스는 어떻게 사용할까?
기본적인 클래스 및 생성자 구조와 다양한 속성들에 대해 기록하였다.


Basic Class

class Player {
    // Class에서 property를 정의할 때는 Type을 지정해준다.
    String name = "JP"; // 외부에서 이름 변경 가능
    final String name = "JP"; // 이러면 외부에서 이름 못바꿈
    int xp = 1500;

    void sayHello() {
        var name = "JPJP";
        // Dart의 클래스에서는 this를 사용하지 않는다. $this.name이 아닌 그냥 $name을 사용
        print("Hi my name is $name"); 
        // 만약 함수 내부에 새로운 name이 정의되었고 이를 사용하려면 아래와 같이 사용한다.
        print("Hi my name is ${this.name}")
    }
}

void main() {
    var player = Player();
    player.name; // JP
    player.xp;  // 1500
}

Constructors

class Player {
    late final String name;
    int xp;

    // Dart에서 Constructor는 아래와 같이 정의
    Player(String name, int xp) {
        this.name = name;
        this.xp = xp;
    }

    // 위 Player Constructor와 같음
    Player(this.name, this.xp);
}

void main() {
    var player = Player("JP", 31);
}

Named Constructor Parameters

class Player {
    late final String n ame;
    int xp, age;
    String team;

    // 위 Player Constructor와 같음
    // 이러면 각 인자가 null로 들어올 수 있다고 에러가 난다.
    // default 값을 정해주거나, required 수식어를 붙여준다.
    Player({this.name = "Default", required this.xp, required this.team, required this.age});
}

void main() {
    // 아래 방법은 positional 이므로 함수처럼 optional로 설정 가능하다.
    var player = Player("JP", 31, "red", 12);

    // optional
    var player = Player(name:"JP", age:12, team:"red", xp:31);
}

Named Constructor

class Player {
    late final String name;
    int xp, age;
    String team;

    Player({
        required this.name, 
        required this.xp, 
        required this.team, 
        required this.age
    });

    // 아래와 같은 코드로 용도에 맞는 생성자를 구성할 수 있다.
    // ':'을 사용함으로써 Player 객체를 원하는 설정에 맞게 초기화하는 것으로 이해하자.
    Player.createBluePlayer({required String name, required int age
    }) : 
    this.age = age, 
    this.name = name,
    this.team = 'blue',
    this.xp = 0;

    Player.createRedPlayer(String name, int age) : 
    this.age = age, 
    this.name = name,
    this.team = 'red',
    this.xp = 0;
}

void main() {
    var bluePlayer = Player.createBluePlayer(name:"JP", age:21);
    var redPlayer = Player.createRedPlayer("EL", 23);
}

Cascade Notation

class Player {
    late final String name;
    int xp, age;
    String team;

    Player({
        required this.name, 
        required this.xp, 
        required this.team, 
        required this.age
    });
}

void main() {
    // 생성자 인스턴스를 만들고 맨 마지막에 세미콜론을 생략한 채 .. + 속성에 접근하여 수정할 수 있다.
    // 이것이 바로 Cascade Notation이다
    var user = Player(name:"JP", xp:0, team:"blue", age:21)
    ..name = 'EL'
    ..xp = 9999
    ..team = 'red';
}

Enums

// Enum을 지정해줌으로써 파라미터를 잘못 넘기는 것을 방지해준다.
// 마치 TypeScript에서 인자로 받을 수 있는 파라미터를 지정해 주는 것과 같다.
enum Team {red, blue}

class Player {
    late final String name;
    int xp, age;
    // enum으로 정의된 Team을 타입으로 설정했기 때문에 team은 red or blue만 받을 수 있다.
    Team team;

    Player({
        required this.name, 
        required this.xp, 
        required this.team, 
        required this.age
    });
}

void main() {    
    var user = Player(name:"JP", xp:0, team:"blue", age:21)
    ..name = 'EL'
    ..xp = 9999
    ..team = Team.blue; // 변경도 enum 범위 안에서 변경해야 한다.
}

Abstract Classes

// Human 추상 클래스는 walk라는 메서드를 가지고 있다.
// 기능이 정의되지는 않았지만 일반 클래스에서 Human 추상메서드를 구현하면 walk 메서드를 꼭 구현해야한다.
abstract class Human {
    void walk();
}

class Player extends Human {
    // extends는 상속이기 때문에 walk 함수의 내용이 채워져 있다면 재정의를 하지 않아도 된다.
    // 그러나 이번 케이스에서는 Human -> walk 메서드가 정의만 되어있어서 재정의 필요
    void walk() {
        print('im walking');
    }
}

class Coach implements Human {
    // implements는 인터페이스 구현이기 때문에 무조건 walk 메서드 재정의가 필요하다.
    void walk() {
        print('the coach is walking');
    }
}

Inheritance

class Human {
    final String name;
    Human(this.name);
    void sayHellao() {
        print('Hi my name is $name');
    }
}

enum Team {red, blue}

class Player extends Human {
    final Team team;

    // 생성자 뒤에 : super 키워드를 통해 부모 클래스와 상호작용 할 수 있다.
    Player({
        required this.team,
        required String name
    }) : super(name);

    // override 키워드 안써줘도됌.
    @override
    void sayHello() {
        super.sayHello();
        print("and I play for ${team}");
    }
}

void main() {
    var player = Player(team:Team.red, name:'JP');
    player.sayHello();
}

Mixins

enum Team {red, blue}

// mixin은 아래와 같이 선언한다.
// 생성자가 존재하지 않아야 한다.
mixin Strong {
  final double strengthLevel = 1500.99;
}

mixin QuickRunner {
  void runQuick() {
    print("runnnnnn!");
  }
}

mixin Tall {
  final double height = 1.99;
}

// mixin 클래스를 사용하려면 with 키워드를 사용한다.
class Player with Strong, QuickRunner, Tall {
  final Team team;

    Player({
        required this.team,
        required String name
    });
}

class Horse with Strong, QuickRunner {
  //...
}

void main() {
  var player = Player(name:"JP", team:Team.red);
  //player는 Player 클래스 인스턴스이면서 QuickRunner mixin 클래스를 사용하므로 runQuick 함수 사용이 가능하다.
  player.runQuick();
}

'Mobile' 카테고리의 다른 글

[Flutter] Flutter 설치방법  (0) 2023.10.12
[Flutter] Flutter의 동작 방식  (0) 2023.10.10
[Dart] Dart의 Function 사용법  (0) 2023.10.10
[Dart] Dart의 Data Types  (0) 2023.10.09
[Dart] Dart의 변수 정의  (3) 2023.10.09

+ Recent posts