주노 님의 블로그

20240930 본캠프 53일차 TIL 본문

TIL

20240930 본캠프 53일차 TIL

juno0432 2024. 9. 30. 23:02

본캠프 53일차 내용 간단요약

  • 09:00 ~ 10:00 : 코드카타
  • 10:00 ~ 12:00 : 서비스팀 회의
  • 12:00 ~ 13:00 : 점심시간
  • 13:00 ~ 18:00 : docker 강의
  • 18:00 ~ 19:00 : 저녁시간
  • 19:00 ~ 21:00 : 챌린지반 수업

오늘 해야할 일 ✔️ 🔺 ❌

 

✔️docker 완강


DOCKER 강의

더보기

 github actions 사용해보기

  • .github/workflows에 yaml파일을 넣는다
    name: GitHub Actions Demo
    run-name: ${{ github.actor }} is testing out GitHub Actions 🚀
    on: [push]
    jobs:
      Explore-GitHub-Actions:
        runs-on: ubuntu-latest
        steps:
          - run: echo "🎉 The job was automatically triggered by a ${{ github.event_name }} event."
          - run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by GitHub!"
          - run: echo "🔎 The name of your branch is ${{ github.ref }} and your repository is ${{ github.repository }}."
          - name: Check out repository code
            uses: actions/checkout@v4
          - run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner."
          - run: echo "🖥️ The workflow is now ready to test your code on the runner."
          - name: List files in the repository
            run: |
              ls ${{ github.workspace }}
          - run: echo "🍏 This job's status is ${{ job.status }}."


  •  github repository에서 actions에 들어가보면
    깃허브 액션이 자동으로 실행된다

github actions를 활용한 ci 구축하기

  • 작성된 프로젝트에서
    # Actions 이름 github 페이지에서 볼 수 있다.
    name: Run Test
    
    # Event Trigger 특정 액션 (Push, Pull_Request)등이 명시한 Branch에서 일어나면 동작을 수행한다.
    on:
      push:
        # 배열로 여러 브랜치를 넣을 수 있다.
        branches: [ develop, feature/* ]
      # github pull request 생성시
      pull_request:
        branches:
          - develop # -로 여러 브랜치를 명시하는 것도 가능
    
      # 실제 어떤 작업을 실행할지에 대한 명시
    jobs:
      build:
        # 스크립트 실행 환경 (OS)
        # 배열로 선언시 개수 만큼 반복해서 실행한다. ( 예제 : 1번 실행)
        runs-on: [ ubuntu-latest ]
    
        # 실제 실행 스크립트
        steps:
          # uses는 github actions에서 제공하는 플러그인을 실행.(git checkout 실행)
          - name: checkout
            uses: actions/checkout@v4
    
          # with은 plugin 파라미터 입니다. (java 17버전 셋업)
          - name: java setup
            uses: actions/setup-java@v2
            with:
              distribution: 'adopt' # See 'Supported distributions' for available options
              java-version: '17'
    
          - name: make executable gradlew
            run: chmod +x ./gradlew
    
          # run은 사용자 지정 스크립트 실행
          - name: run unittest
            run: |
              ./gradlew clean test
    


    .github/workflows/~~.yaml파일에 위 내용을 넣는다
    이름은 run test이며
    develop와 feature의 모든하위브랜치에서 push가 발생하거나 develop로 pull request가 발생시
    ubuntu-lastest에서 git checkout을 실행후, java17버전으로 unittest 라는 스크립트를 테스트한다.
    github actions에 등록이 된것을 볼 수 있다.

  • 사용법
    pull request시 자동으로 테스트 하게된다.
    만약 테스트가 실패하게 된다면 아래와같은 오류와 메일이 보내지게 된다.

 

 

github actions를 활용한 cd 구축하기

  • cloud type라는 무료 서비스를 사용한다.
  • 흐름
    개발자
    1. 개발자는 feature/ 로 시작하는 브랜치에서 test코드를 포함한 수정 작업을 완료한뒤 pull request를 생성
    자동화
    2. pull request를 만들면 해당 브랜치에 대해 gradle test를 수행
    개발자
    3. pull request 코드의 test가 실패한 경우, pull request를 생성한 개발자는 test 코드를 수정하여 pull request를 변경
    4. pull request 코드의 test가 성공한 경우, 다른 개발자들의 승인을 기다림
    5. 다른 개발자들은 pull request의 코드를 승인하거나 댓글로 소통
    자동화
    6. main 브랜치에 merge되면 해당 브랜치를 cloudtype 서버에 배포함
  • cloud type 회원가입 및 카드등록
  • CLOUDTYPE 깃허브 저장소 배포하기
    알맞는 버전, 알맞는 포트로 배포를 하면된다.

  • 레파지토리에 시크릿 키 등록하기
    repository > secrets and variables > actions에 
    깃허브 토큰과, cloudtype의 토큰을 저장한다
    배포가 되는것을 볼 수 있다 

  • 정리
    아래의 코드에 의해 pull request가 열린다면
    테스트 코드를 실행시켜 통과되는것을 확인하고. 
    wname: test every pr
    on:
      workflow_dispatch:
      pull_request:
    permissions:
      contents: read
      pull-requests: read
    jobs:
      deploy:
        runs-on: ubuntu-latest
        steps:
          - name: Checkout
            uses: actions/checkout@v3
          - name: setup jdk
            uses: actions/setup-java@v3
            with:
              java-version: '17'
              distribution: 'temurin'
              cache: gradle
          - name: Grant execute permission for gradlew
            run: chmod +x ./gradlew
          - name: gradlew test
            run: ./gradlew test

    정상적으로 동작하여 pr을 수행한다면
    main에 merge시 아래코드가 동작한다
    name: Deploy to cloudtype
    on:
      workflow_dispatch:
      push:
        branches:
          - main
    jobs:
      deploy:
        runs-on: ubuntu-latest
        steps:
          - name: Checkout
            uses: actions/checkout@v3
          - name: Connect deploy key
            uses: cloudtype-github-actions/connect@v1
            with:
              token: ${{ secrets.CLOUDTYPE_TOKEN }}
              ghtoken: ${{ secrets.GHP_TOKEN }}
          - name: Deploy
            uses: cloudtype-github-actions/deploy@v1
            with:
              token: ${{ secrets.CLOUDTYPE_TOKEN }}
              project: jojjh2/docker-cicd-study
              stage: main
              yaml: |
                name: docker-cicd-study
                app: java@17
                options:
                  ports: 8080
                context:
                  git:
                    url: git@github.com:${{ github.repository }}.git
                    ref: ${{ github.ref }}
                  preset: java-springboot

    위 코드대로 main에 push가 되면 자동으로 cloud type에 배포가 된다.

    요약정리하면
    ci :  지속적 통합
    pr이 생성될때 테스트코드를 실행하는 과정

    cd : 지속적 배포
    merge가 되었을때 변경사항이 자동으로 배포
    이다.

dockerfile을 사용하여  docker image 생성하기

  • docker file 이란?
    docker image를 빌드하기 위한 파일이다.
    어떤 파일을 실행할지, 어떤 프로그램을 설치할지, 어떤 os에서 실행할지 등등에 관한 정보가 있다.
    build를 하게되면, 설정파일에 있는대로 필요한 내용을 설치하게된다.
    이 image를 실행하게되면 docker container가 생성된다.
    # Dockerfile
    FROM ubuntu:latest
    MAINTAINER Your Name <your-email@example.com>
    RUN apt-get update && apt-get install -y nginx
    COPY index.html /usr/share/nginx/html
    EXPOSE 80
    CMD ["nginx", "-g", "daemon off;"]

    from : 베이스 이미지를 선택, 여기서는 ubuntu 최신버전
    maintainer : 이미지를 만든 사람의 정보를 입력
    run : 이미지를 생성하는동안 실행할 명령어, 여기서는 패키지 목록을 업데이트하고 nginx를 설치한다.
    copy : 파일을 이미지에 복사, 여기서는 index.html 파일을 docker의 nginx의 디렉토리에 복사한다
    expose : 컨테이너가 노출할 포트 설정, 여기서는 80포트를 노출한다
    cmd :  컨테이너가 실행될 때 실행할 명령 설정, 여기서는 포그라운드에서 실행되게 설정한다.

  • 도커 이미지 생성방법
    docker build -t my-nginx:latest 처럼 실행한다.

    생성된 도커 이미지 실행방법
    docker run -d -p 80:80 my-nginx:latest

    컨테이너 종료방법
    docker stop my-nginx

  • 그외 dockerfile 명령어
    lable : 이미지에 메타데이터 추가
    LABEL purpose= 'nginx test'

    ENTRYPOINT : 컨테이너를 시작할때, 실행할 명령어 입력
    컨테이너를 실행할때마다 실행하며, 추가적인 명령어 존재 여부와 상관없이 무조건 실행
    ENTRYPOINT ["npm", "strart"]

    ENV : 환경 변수를 설정
    이미지 안에 각종 환경 변수를 지정
    ENV STAGE staging

    WORKDIR : 작업 디렉터리를 지정
    WORKDIR /app

    USER : 사용자를 설정
    container의 기본 사용자는 root임
    RUN["useradd", "user"]
    USER user

docker compose

  • docker compose를 통해 여러개의 컨테이너를 관리한다

    사용해야하는 이유
    간편한 설정 : 여러 컨테이너를 한 파일에 적어서 설정 가능함
    자동 배포 : 설정 파일이 있으면 compose가 알아서 컨테이너들을 만들어 줌
    의존성 관리 : 서로 의존하는 관계에 있으면 docker compose가 이를 관리해줌
    모니터링과 로깅 : 컨테이너들의 로그를 모아줌
    확장성 : 여러 컨테이너를 하나의 그룹으로 관리해줌
    유연성 : 개발환경, 테스트환경, 실제 운영 환경에서도 같은 설정 파일을 써서 일관성을 유지해줌
    보안 강화 : 컨테이너들의 네트워크를 분리해 외부로부터의 접근을 제한함
    유지보수가 쉬움 : 설정 파일 하나로 관리가 가능

  • 사용하는 방법
    개발환경에서의 사용 : 앱을 개발할때 따로 떼어 놓고 실행할수 있음
    자동화된 테스트 환경에서 : 테스트를 위한 별도의 환경을 쉽게 만들고 없앨 수 있다
    단일 호스트 배포에서 : 트래픽이 적은 환경에서 실제 배포에 사용도 가능하다

  • 장점
    한번에 여러 컨테이너를 설정 가능, 빠른 서비스 실행 가능, 같은 네트워크에서 쉽게 연결 가능

  • docker compose 실행하는법
    1. 각 애플리케이션의 dockerfile 작성하기
    2. docker-compose.yaml 작성하기
    3. docker compose up으로 실행하기

  • yaml파일이란?
    컴퓨터가 읽을수 있고, 사람이 읽기에도 쉬운 텍스트 형식이다.
    일반 텍스트로 써져있으며, 목록 키-값 쌍으로 이루어져 있다
    설정을 정리하고 관리하기에 좋으며,
    docker에서 여러 컨테이너의 설정을 한곳에 쉽게 정리 할 수 있다.
    들여쓰기가 잘못된 경우 yaml파일이 의도와 다르게 해석할 수 있으니 주의해야하며
    https://www.yamllint.com/
    에서 들여쓰기 검사를 해준다.

  • docker compose 파일 설명
    #docker compose의 버전
    version: '3'
    # 네개의 서비스를 정의한다, web, api, redis, mysql
    services:
    #web 서비스는 nginx:lkatest 이미지를 사용하고, 80포트를 매핑한다.
    #web 서비스는 현재 디렉토리 내 web디렉토리를 컨테이너에 연결한다, api 서비스에 의존하며, api서비스가 먼저 실행된 후에 실행된다
      web:
        image: nginx:latest
        ports:
          - 80:80
        volumes:
          - ./web:/usr/share/nginx/html
        depends_on:
          - api
        links:
          - api:api
    #api 서비스는 java 최신버전의 이미지를 사용하고, api를 컨테이너 내부의 app 경로에 매핑한다.
    #8080포트를 노출시키며, 각각의 호스트 이름을 설정하는 환경변수가 있으며 mysql과 redis에 의존하여 mysql과 redis 실행 후 실행된다.
      api:
        image: java:latest
        volumes:
          - ./api:/app
        ports:
          - 8080:8080
        environment:
          - REDIS_HOST=redis
          - MYSQL_HOST=mysql
          - MYSQL_USER=root
          - MYSQL_PASSWORD=password
          - MYSQL_DATABASE=test
        depends_on:
          - mysql
          - redis
        links:
          - mysql:mysql
          - redis:redis
    #redis최신 이미지를 사용하며, 포트는  6379포트를 노출시킨다
      redis:
        image: redis:latest
        ports:
          - 6379:6379
    #mysql 최신 이미지를 사용하며, 포트는 3306포트를 사용하고 mysql을 컨테이너 내부의 var/lib/mysql에 위치시킨다
    #비밀번호와 데이터베이스명, 기본사용자와 패스워드를 설정하는 환경변수를 설정한다.
      mysql:
        image: mysql:latest
        ports:
          - 3306:3306
        volumes:
          - ./mysql:/var/lib/mysql
        environment:
          - MYSQL_ROOT_PASSWORD=password
          - MYSQL_DATABASE=test
          - MYSQL_USER=root
          - MYSQL_PASSWORD=password

    yaml파일
    version: "3"
    services:
      web:
        build:
          context: .    # Dockerfile 의 위치
          dockerfile: Dockerfile    # Dockerfile 파일명
        container_name: testapp_web_1    # 생략하는 경우 
        # 자동으로 부여 docker run 의 --name 옵션과 동일
        
        #8080포트를 8080호스트 머신과 연결한다
        ports: "8080:8080"  # docker run 의 -p 옵션과 동일
        
        #호스트머신과의 연결이 아닌 링크로연결된 서비스 간 통신이 필요할때 사용한다
        expose: "8080"   
        
        # networks 를 최상위에 정의한다면 해당 이름을 사용 docker run의 --net 옵션과 동일
        networks: testnetwork    
        
        # 현재 디렉토리를 html 디렉토리와 연결하고 있음 docker run 의 -v 옵션과 동일
        volumes: .:/var/lib/nginx/html
        
        #환경변수
        environment:
          - APPENV=TEST    # docker run 의 -e옵션과 동일
    # 컨테이너를 실행할때 어떤 명령어를 실행할지 docker run 의 가장 마지막
        command: npm start
    #중지된 경우에 재시작을 하나? docker run 의 --restart 옵션과 동일
        restart: always
        depends_on: db    # 이 옵션에 지정된 서비스가 시작된 이후에 `web`서비스가 실행
        links: db # Docker가 네트워크를 통해 컨테이너를 연결하도록 정의합니다. 
        # 컨테이너를 연결할 때 Docker는 환경 변수를 만들고 
        # 컨테이너를 알려진 호스트 목록에 추가하여 서로를 검색할 수 있도록 합니다.
        deploy:    # 서비스의 복제본 개수 등 지정
          replicas: 3
          mode: replicated
      service2:
        # ...service2 설정
    #컨테이너가 소속된 네트워크를 지정할수있으며 따로 지정하지않을경으 default_${project}와 같이 지정한다, 기본적으로 컨테이너는 같은 네트워크에 있어야 서로 통신이 가능하다
    networks:
    #도커 볼륨 혹은 호스트 볼륨을 마운트하여사용한다. 도커 볼륨의 경우 compose.yml 파일에 선언된 볼륨만 docker- compose.yml에서 사용할 수 있다.
    volumes:
      logvolume01: {} # 도커볼륨 logvolume01 선언

docker 모니터링과 로깅

  • 모니터링이란?
    컨테이너의 상태등을 확인한다

  • 왜 중요한가?
    잘 돌아가는지, 어떤 문제가있는지, 메모리는 많이 잡아먹지않는지 파악 할 수 있다
    문제를 빨리 파악하고 해결 할 수 있다.

  • 어떻게 하는가?
    docker stats 명령어를 사용하면 실시간으로 확인 가능하다
    특정 컨테이너의 상태만 보고 싶다면 docker stats [컨테이너 이름 또는 id]
    CONTAINER ID   NAME                         CPU %     MEM USAGE / LIMIT    MEM %     NET I/O       BLOCK I/O   PIDS
    8b4172b8f519   spring-boot-sample-nginx-1   0.00%     18.57MiB / 15.5GiB   0.12%     1.57kB / 0B   0B / 0B     25
    999c364cccaa   spring-boot-sample-mysql-1   16.69%    204.3MiB / 15.5GiB   1.29%     1.57kB / 0B   0B / 0B     1
    a637da33772f   spring-boot-sample-redis-1   0.17%     8.883MiB / 15.5GiB   0.06%     1.57kB / 0B   0B / 0B     6


  • htop
    시스템 모니터링의 도구
    컴퓨터의 cpu 메모리 사용량 같은 정보를 실시간으로 볼 수 있음.


  • df
    시스템 전체의 디스크 사용량을 확인 가능하다

  • du
    디렉토리 별로 사용  공간을 나타내준다

container 로깅

  • docker는 모든 컨테이너의 출력 또는 에러를 캡쳐하여 json-file 로깅 드라이버를 사용하여 json형식으로 파일에 기록함.
    /var/lib/docker/containers/[컨테이너 id]/[컨테이너id]-json.log에 기록됨

    로그 전체 출력하기
    docker logs [컨테이너 이름]

    지속적인 로그 출력하기
    docker logs -f [컨테이너 이름]

docker volume

  • 사용 이유
    1. 데이터 영속성 : 컨테이너는 쓰고 나면 사라지는데 볼륨은 컨테이너가 사라져도 데이터는 남기때문에
    2. 같은 데이터를 여러 컨테이너가 사용할 수 있게 : 볼륨으로 여러 컨테이너가 하나의 데이터를 사용할 수 있음
    3. 데이터를 백업하고 옮기기 쉽게 : 백업하기 편함
    4. 데이터를 안전하게 보관 : 중요한 데이터를 컨테이너밖으로 옮김으로써 안전하게 보관할 수 있음
    5. 코드와 데이터의 분리 : 코드를 바꿔도 데이터는 유지 가능해서 개발할때 편함

  • 장점
    1. 백업의 간편함
    2. docker 명령어로 관리한다
    3. 어떤 컨테이너에도 잘돌아간다
    4. 여러 컨테이너에서 사용 가능하다

  • volume
    /var/lib/docker/volumes/ 에 만들어진다

  • bind mount
    컴퓨터의 특정 폴더나 파일을 골라서 docker 컨테이너에 붙여넣는것
    절대 경로를 사용한다

  • ympfs mount
    컴퓨터 메모리를 사용해서 일시적인 데이터를 저장한다
    영구적으로 저장하는게 아닌 임시적인 저장이나, 메모리를 사용하므로 빨라진다
    다만, 컨테이너끼리 공유는 되지않다

  • 영속성 테스트
    user@localhost:~$ docker container run -ti --rm -v datavol:/data alpine
    Unable to find image 'alpine:latest' locally
    latest: Pulling from library/alpine
    43c4264eed91: Already exists
    Digest: sha256:beefdbd8a1da6d2915566fde36db9db0b524eb737fc57cd1367effd16dc0d06d
    Status: Downloaded newer image for alpine:latest
    / # echo "볼륨 데모" > /data/memo.txt
    / # exit
    user@localhost:~$ docker container ls
    CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
    user@localhost:~$ docker container run --rm -v datavol:/data ubuntu cat /data/demo.txt
    Unable to find image 'ubuntu:latest' locally
    latest: Pulling from library/ubuntu
    dafa2b0c44d2: Pull complete
    Digest: sha256:dfc10878be8d8fc9c61cbff33166cb1d1fe44391539243703c72766894fa834a
    Status: Downloaded newer image for ubuntu:latest
    cat: /data/demo.txt: No such file or directory
    user@localhost:~$ docker container run --rm -v datavol:/data ubuntu cat /data/memo.txt
    볼륨 데모

    image를 한번 실행후 거기서 /data/memo.txt에 파일을 만들었다.
    image를 실행 종료하고, 다른 image를 생성한 후, memo.txt를 불러보았다.
    그러니 볼륨 데모라는 메세지가 출력되었다.
    이렇게 volume는 데이터를 남겨준다.,

docker network

  • 도커 네트워크는 컨테이너들끼리의 통신을 쉽게하고 보안을 강화하는 역할을 한다.
    실제 비즈니스 애플리케이션에는 여러 컨테이너가 협력해야하므로, 이를위해 서로간의 통신 경로인 네트워크가 필요하다.
    도커는 이를위해 컨테이너 네트워크 모델 CNM을 제공한다.
    출처 : 내일배움캠프 도커 5주차 2강
    샌드박스 : 외부로부터 컨테이너를 격리한다.
    엔드포인트 : 샌드박스와 네트워크를 연결하는 지점이다.
    네트워크 : 컨테이너 간의 통신을 가능하게 하는 경로이다.

  • docker network 종류
    브리지 네트워크 : docker의 기본 네트워크, 단일 호스트 내에서 여러 docker 컨테이너가 통신할수있게 해준다.
    docker가 각 컨테이너에 ip주소를 자동으로 할당한다, 외부 네트워크와는 분리되어 있지만, 포트 매핑을 통해 외부에서 접근 할 수 있다.

  • 공용 네트워크
    외부 접근이 필요한 경우 브리지 네트워크에 포트를 열거나 호스트 네트워크를 사용할수 있다.
    호스트 네트워크는 컨테이너가 호스트 컴퓨터의 네트워크를 직접 사용하게 하여, 외부 네트워크와 바로 통신할수 있게 합니다.

  • 사설 네트워크
    특정 컨테이너들끼리만 통신할 수 있도록 격리된 네트워크이다.
    사용자가 직접 네트워크를 정의하고, 필요한 컨테이너만 연결할 수 있어보안이 강화된다.

docker hub repositories에 image push하는법

  • image push
    registry : dockerfile을 통해 이미지를 저장하는 곳
    public registry : 공개적으로 사용할수있는 곳이며 docker hub가 있다
    private registry : 특정한그룹만 이미지를 접근할수있는곳.

  • 순서
    docker logout
    docker login
    
    #push할 테스트 이미지를 가져온다
    docker image pull nginx:latest
    docker image pull ubuntu:22.04
    
    #push할 이미지에 태그를 달아준다 이떄 본인의 docker hub 계정 이름을 사용해야한다.
    docker image tag nginx:latest 본인 계정명/nginx-test:1.0
    
    #push한다.
    docker push 본인계정명/nginx-test:1.0
    
    #push한 도커 파일을 hub에서 pull 받아온다
    docker pull 본인계정명/nginx-test:1.0
    
    #pull한 도커파일을 실행해본다.
    docker run -d -p 8001:80 --name=nginx-test 본인계정명/nginx-test:1.0


docker 파일 최적화

  • 왜 해야하는가?
    빠르게(다운로드나 업로드할때), 재사용성 높이기, 보안강화, 유지보수가 쉽다

docker container 가상화

  • 컨테이너란?
    앱을 실행하는데 필요한 코드, 언어, 라이브러리등
    docker은 dockerfile을 통해 컨테이너를 만듬

  • 장점
    컴퓨터 자원을 여러앱과 나눠쓸수 있음
    경량화 : 운영체제를 여러 앱과 공유를 하여, 운영체제가 필요없음, 컨테이너 파일은 작고 가벼울수 있음.
    이동성 및 플랫폼 독립성 : 앱을 실행하는데 필요한걸 담기때문에 어디서든 실행가능
    최신 개발 트렌드에 맞음 : 새로운 코드를 조금씩 추가하거나 업데이트할때 유용함

  • 용도
    마이크로 서비스에 좋음 : 컨테이너는 작고 가벼워서 앱을 여러 작은 부분으로 나누어 관리 할 수 있음
    DevOps에 유용함
    하이브리드, 멀티클라우드에 이상적임 : 컨테이너는 어디서든 잘 돌아가기때문에 여러 클라우드를 섞은 멀티 클라우드, 하이브리드등에 이상적임

 


챌린지반 수업

더보기

적어도 자료구조, 컴퓨터구조, 네트워크, 데이터베이스, 운영체제는 기본적으로 가져가야 한다

키워드선정 > 공부 > 실습 > 구두설명

ex 페이지 교체 알고리즘이라는 주제가 있을때

키워드선정 : 메모리 운영체제 자료구조etc...

 

딥한 주제를 너무 깊게는 보지말고

인터넷을 검색했을때 이미지에 공통적으로 나오는 부분이 있다. 그 부분을 공부하면 적어도 그 내용은 무조건 나오겠구나!! 라고 생각


공부해야할것

DOCKER 복습


오늘의 회고 & 12시간 몰입했는가? 

12시간을 공부했으며 이제부터는 왜? 이 기술스택을 사용했는가

어떻게 사용할건지에 대한 공부를 진행해야할것 같다!

'TIL' 카테고리의 다른 글

20241002 본캠프 55일차 TIL  (0) 2024.10.02
20241001 본캠프 54일차 TIL  (1) 2024.10.01
20240927 본캠프 52일차 TIL  (0) 2024.09.27
20240921 본캠프 51일차 TIL  (0) 2024.09.26
20240925 본캠프 50일차 TIL  (0) 2024.09.25