Nest.js

[NestJS] AWS Parameter Store AWS-SDK ํ”„๋กœ์ ํŠธ 3ํƒ„

martinooo 2024. 9. 6. 13:32

AWS

 

 

๐Ÿคท‍โ™‚๏ธ ๋ณธ๋ก ์— ์•ž์„œ


์ด์ „ ๊ธ€์—์„œ AWS Parameter Store ์ƒ์„ฑํ•˜๊ณ  ๋งค๊ฐœ๋ณ€์ˆ˜, ๋ฌธ์ž์—ด ์•”ํ˜ธํ™” ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ดค์Šต๋‹ˆ๋‹ค.

์ด๋ฒˆ๊ธ€์—์„œ๋Š” AWS-SDK๋ฅผ ์‚ฌ์šฉํ•ด์„œ Nest.js ํ”„๋กœ์ ํŠธ์— Parameter Store ์ ‘๊ทผํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

 

[NestJS] AWS Parameter Store AWS-SDK ํ”„๋กœ์ ํŠธ 2ํƒ„

2024.09.05 - [Nest.js] - [NestJS] AWS Parameter Store ํ”„๋กœ์ ํŠธ 2ํƒ„

 

[NestJS] AWS Parameter Store ํ”„๋กœ์ ํŠธ 2ํƒ„

๐Ÿคท‍โ™‚๏ธ ๋ณธ๋ก ์— ์•ž์„œ์ด์ „ ๊ธ€์—์„œ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ์ค‘์•™ ๊ด€๋ฆฌ ํ•˜๋Š” ์ด์œ ์™€ AWS Parameter Store ๊ฐœ๋…๊ณผ ์žฅ์ ์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ดค์Šต๋‹ˆ๋‹ค.์ด๋ฒˆ ๊ธ€์—์„œ๋Š” ์‹ค๋ฌด์—์„œ Parameter Store ํŒŒ๋ผ๋ฏธํ„ฐ ๊ฐ’๋“ค์„ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•

cometruedream.tistory.com

 

[NestJS] AWS Parameter Store AWS-SDK ํ”„๋กœ์ ํŠธ 1ํƒ„

2024.09.04 - [Nest.js] - [NestJS] AWS Parameter Store ํ”„๋กœ์ ํŠธ 1ํƒ„

 

[NestJS] AWS Parameter Store ํ”„๋กœ์ ํŠธ 1ํƒ„

๐Ÿคท‍โ™‚๏ธ ๋ณธ๋ก ์— ์•ž์„œํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋‹ค ๋ณด๋ฉด ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๊ด€๋ฆฌ๊ฐ€ ์–ด๋ ค์›Œ์ง‘๋‹ˆ๋‹ค.ํ”„๋กœ์ ํŠธ ์‚ฌ์ด์ฆˆ๊ฐ€ ์ปค์ง€๋ฉด ๋”์šฑ๋” ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๊ด€๋ฆฌ๊ฐ€ ์–ด๋ ค์›Œ์ง€๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฒˆ ๋ฒ ํƒ€์„œ๋น„์Šค๊นŒ์ง€ ๊ฐœ๋ฐœ์„ ์™„๋ฃŒํ•˜๊ณ  A

cometruedream.tistory.com

 

๐Ÿคท‍โ™‚๏ธ AWS-SDK ์„ธํŒ… ๋ฐ Parameter Store ์ ‘๊ทผ ๋ฐฉ๋ฒ•


Nest.js ํ”„๋กœ์ ํŠธ์—์„œ ์ฝ”๋“œ๋กœ Parameter Store ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋จผ์ € AWS-SDK๋ฅผ ์„ค์น˜ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

 

1. AWS SDK ์„ค์น˜

npm install --save @aws-sdk/client-ssm

 

2. Nest.js config ๊ตฌ์กฐ ์„ธํŒ…

config.module.ts ์ƒ์„ฑ

ConfigModule๋ฅผ ์ „์—ญ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์„œ๋น„์Šค์— ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค.

// config.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import EnvConfig from './env.config';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      envFilePath: `.env`,
      load: [EnvConfig],
    }),
  ],
})
export class AppConfigModule {}

 

env.config.ts ์ƒ์„ฑ

import { AwsConfigService } from './aws-config.service';

export default async () => {
  const awsConfigService = new AwsConfigService();

  await awsConfigService.loadConfig();

  return awsConfigService.getConfig();
};

export default async () => {}๋Š” ์ต๋ช… ํ•จ์ˆ˜๋ฅผ ์„ ์–ธํ•˜๊ณ  ๋‹ค๋ฅธ ํŒŒ์ผ์—์„œ ํ•จ์ˆ˜๋ฅผ import ์‹œ ๋ฐ”๋กœ ํ•จ์ˆ˜๊ฐ€ ๋น„๋™๊ธฐ์ ์œผ๋กœ ์‹คํ–‰๋˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

ConfigModule์— load ์˜ต์…˜์—์„œ env.config.ts ํ•จ์ˆ˜๊ฐ€ ๋น„๋™๊ธฐ์ ์œผ๋กœ ์ฆ‰์‹œ ์‹คํ–‰ํ•ด์„œ ์„ค์ • ๊ฐ’์„ ์„ธํŒ…ํ•ด์ฃผ๋Š” ๋กœ์ง์ž…๋‹ˆ๋‹ค.

 

aws-config.service.ts ์ƒ์„ฑ

import { GetParametersByPathCommand, SSMClient } from '@aws-sdk/client-ssm';
import { Logger } from '@nestjs/common';

export class AwsConfigService {
  private readonly ssmClient: SSMClient;
  private config: Record<string, string> = {};
  private logger = new Logger(AwsConfigService.name);

  constructor() {
    this.ssmClient = new SSMClient({
      region: process.env.AWS_REGION,
      credentials: {
        accessKeyId: process.env.AWS_ENV_ACCESS_KEY,
        secretAccessKey: process.env.AWS_ENV_SECRET_ACCESS_KEY,
      },
    });
  }

  // ๋กœ์ปฌ ENV / AWS ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋„๋ก ๊ตฌ์„ฑ
  public async loadConfig() {
    try {
      const env = process.env.NODE_ENV || 'development';
      const path = `/YOUR PATH/${env}/`;

      const getAllParameters = async (path: string) => {
        let parameters = [];
        let nextToken: string | undefined;

        do {
          const command = new GetParametersByPathCommand({
            Path: path,
            Recursive: true,
            WithDecryption: true,
            NextToken: nextToken, // NextToken์„ ์‚ฌ์šฉํ•˜์—ฌ ํŽ˜์ด์ง€๋„ค์ด์…˜ ์ฒ˜๋ฆฌ
          });

          const response = await this.ssmClient.send(command);

          if (response.Parameters) {
            parameters = parameters.concat(response.Parameters);
          }

          nextToken = response.NextToken; // ๋‹ค์Œ ํŽ˜์ด์ง€๊ฐ€ ์žˆ์œผ๋ฉด NextToken์ด ์„ค์ •๋จ
        } while (nextToken);

        return parameters;
      };

      const result = await getAllParameters(path);

      if (result) {
        for (const param of result) {
          if (param.Name && param.Value) {
            const key = param.Name.replace(path, '');
            this.config[key] = param.Value.replace(/\\\\n/g, '\\n');
          }
        }
      }
    } catch (error) {
      this.logger.error('AWS SSM ์ ‘๊ทผ ์‹คํŒจ', error);
    }
  }

  get(key: string): string {
    // ๋กœ์ปฌ์—์„œ ์‹คํ–‰ํ•  ๊ฒฝ์šฐ
    if (process.env.ENV_LOCAL === 'true') {
      return process.env[key] || '';
    }

    return this.config[key] || process.env[key] || '';
  }

  getConfig() {
    return {
      redis: {
        url: `redis://${this.get('REDIS_URL')}:${this.get('REDIS_PORT')}`,
      },
      AWS_REGION: this.get('AWS_REGION'),
      AWS_S3_ACCESS_KEY: this.get('S3_ACCESS_KEY_ID'),
      AWS_S3_SECRET_ACCESS_KEY: this.get('S3_SECRET_ACCESS_KEY'),
      AWS_S3_BUCKET_NAME: this.get('AWS_S3_BUCKET_NAME'),
      NICE_API_KEY: this.get('NICE_API_KEY'),
      firebase: {
        type: this.get('FIREBASE_TYPE'),
        project_id: this.get('FIREBASE_PROJECT_ID'),
        private_key_id: this.get('FIREBASE_PRIVATE_KEY_ID'),
        private_key: this.get('FIREBASE_PRIVATE_KEY'),
        client_email: this.get('FIREBASE_CLIENT_EMAIL'),
        client_id: this.get('FIREBASE_CLIENT_ID'),
        auth_uri: this.get('FIREBASE_AUTH_URI'),
        token_uri: this.get('FIREBASE_TOKEN_URI'),
        auth_provider_x509_cert_url: this.get('FIREBASE_AUTH_PROVIDER_X509_CERT_URL'),
        client_x509_cert_url: this.get('FIREBASE_CLIENT_x509_CERT_URL'),
        universe_domain: this.get('FIREBASE_UNIVERSE_DOMAIN'),
      },
    };
  }
}

 

SSMClient ์ดˆ๊ธฐ ์…‹ํŒ…

constructor() {
    this.ssmClient = new SSMClient({
      region: process.env.AWS_REGION,
      credentials: {
        accessKeyId: process.env.AWS_ENV_ACCESS_KEY,
        secretAccessKey: process.env.AWS_ENV_SECRET_ACCESS_KEY,
      },
    });
  }
  • SSMClient ์ธ์Šคํ„ด์Šค๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
  • ์„œ๋น„์Šค ์ƒ์„ฑ์ง€์—ญ region๊ณผ ๋ฐœ๊ธ‰๋ฐ›์€ accessKeyId, secretAccessKey๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.
  • accessKeyId, secretAccessKey ํ”„๋กœ์ ํŠธ .env ํŒŒ์ผ์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

 

GetParametersByPathCommand API ํ™œ์šฉ

  const command = new GetParametersByPathCommand({
    Path: path,
    Recursive: true,
    WithDecryption: true, //์•”ํ˜ธํ™”๋œ ํŒŒ๋ผ๋ฏธํ„ฐ์ธ ๊ฒฝ์šฐ true๋กœ ์„ค์ •
    NextToken: nextToken, // NextToken์„ ์‚ฌ์šฉํ•˜์—ฌ ํŽ˜์ด์ง€๋„ค์ด์…˜ ์ฒ˜๋ฆฌ
  });
  • GetParametersByPathCommand API ํ™œ์šฉํ•ด์„œ Parameter Store์— ๊ฐ’๋“ค์„ ๋ถˆ๋Ÿฌ์˜ต๋‹ˆ๋‹ค.
  • GetParameterCommand ๋ง๊ณ  GetParametersByPathCommand ์‚ฌ์šฉํ•œ ์ด์œ ๋Š” Path ๋ณ„๋กœ Parameter Store ๊ฐ’๋“ค์€ ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•ด์„œ์ž…๋‹ˆ๋‹ค.
  • IAM ๊ถŒํ•œ์— ๋”ฐ๋ผ์„œ Parameter Store ์ ‘๊ทผ API ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

 

GetParametersByPath ์‹ค๋ฌด์—์„œ ์‚ฌ์šฉ๋ฒ•

const getAllParameters = async (path: string) => {
    let parameters = [];
    let nextToken: string | undefined;

    do {
      const command = new GetParametersByPathCommand({
        Path: path,
        Recursive: true,
        WithDecryption: true,
        NextToken: nextToken, // NextToken์„ ์‚ฌ์šฉํ•˜์—ฌ ํŽ˜์ด์ง€๋„ค์ด์…˜ ์ฒ˜๋ฆฌ
      });

      const response = await this.ssmClient.send(command);

      if (response.Parameters) {
        parameters = parameters.concat(response.Parameters);
      }

      nextToken = response.NextToken; // ๋‹ค์Œ ํŽ˜์ด์ง€๊ฐ€ ์žˆ์œผ๋ฉด NextToken์ด ์„ค์ •๋จ
    } while (nextToken);

    return parameters;
  };

 

GetParametersByPath API ์‚ฌ์šฉ ์‹œ ์œ ์˜์‚ฌํ•ญ

  1. ๋ฐ˜ํ™˜ํ•  ํ™˜๊ฒฝ๋ณ€์ˆ˜ ํ•ญ๋ชฉ ์ˆ˜: ์ด API๋Š” ํ•œ ๋ฒˆ์˜ ์š”์ฒญ์œผ๋กœ ์ตœ๋Œ€ 10๊ฐœ์˜ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ํ•ญ๋ชฉ์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. (์ตœ์†Œ 1๊ฐœ, ์ตœ๋Œ€ 10๊ฐœ)
  2. NextToken ํ™œ์šฉ: ๋งŒ์•ฝ ์š”์ฒญ ๊ฒฝ๋กœ์— 10๊ฐœ ์ด์ƒ์˜ ํ™˜๊ฒฝ๋ณ€์ˆ˜๊ฐ€ ์กด์žฌํ•˜๋Š” ๊ฒฝ์šฐ, ๋ฐ˜ํ™˜๋œ ์‘๋‹ต์— NextToken์ด ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. ์ด NextToken์„ ์ด์šฉํ•ด ์ถ”๊ฐ€ ์š”์ฒญ์„ ๋ณด๋‚ด๋ฉด, 10๊ฐœ ์ด์ƒ์˜ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ์ˆœ์ฐจ์ ์œผ๋กœ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

GetParametersByPathCommand ๊ณต์‹๋ฌธ์„œ

https://docs.aws.amazon.com/systems-manager/latest/APIReference/API_GetParametersByPath.html#systemsmanager-GetParametersByPath-request-MaxResults

 

GetParametersByPath - AWS Systems Manager

If a user has access to a path, then the user can access all levels of that path. For example, if a user has permission to access path /a, then the user can also access /a/b. Even if a user has explicitly been denied access in IAM for parameter /a/b, they

docs.aws.amazon.com

 

 

 

๐Ÿคท‍โ™‚๏ธ ๋งˆ์ง€๋ง‰์œผ๋กœ


ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ์ค‘์•™์—์„œ ๊ด€๋ฆฌํ•˜๋Š” ๊ณผ์ •๊ณผ AWS์˜ Parameter Store ๊ธฐ๋Šฅ, AWS SDK ์‚ฌ์šฉ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ๋‹ค๋ค˜์Šต๋‹ˆ๋‹ค.

 

.env ํŒŒ์ผ ๊ด€๋ฆฌ์™€ ์ค‘์•™ ์ง‘์ค‘์‹ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๊ด€๋ฆฌ ์ค‘ ์–ด๋Š ๊ฒƒ์ด ๋” ์ข‹์€์ง€๋Š” ์ •๋‹ต์ด ์—†์Šต๋‹ˆ๋‹ค.

๊ฐ๊ฐ์˜ ๋ฐฉ๋ฒ•์—๋Š” ์žฅ๋‹จ์ ์ด ์žˆ์œผ๋ฉฐ, ํŒ€ ๋ฌธํ™”์™€ ํ”„๋กœ์ ํŠธ ์šด์˜ ๋ฐฉ์‹์— ๋งž์ถฐ ์„ ํƒํ•˜๋Š” ๊ฒƒ์ด ์ตœ์„ ์ž…๋‹ˆ๋‹ค.

 

์ €๋Š” Parameter Store์˜ ์•ˆ์ „์„ฑ, ์ค‘์•™ ๊ด€๋ฆฌ, ์ž๋™ํ™” ๊ฐ€๋Šฅ์„ฑ์— ๋Œ€ํ•ด ๊ธ์ •์ ์œผ๋กœ ํ‰๊ฐ€ํ•˜๋ฉฐ ์•ž์œผ๋กœ๋„ ํ”„๋กœ์ ํŠธ์— ์ ๊ทน ํ™œ์šฉํ•  ๊ณ„ํš์ž…๋‹ˆ๋‹ค.

 

 


 

์žฌ๋ฐŒ๋Š” ์˜ค๋Š˜์˜ ๋ ๋ณ„ ์šด์„ธ ๋ณด๊ณ  ๊ฐ€์„ธ์š”! 

 

 

https://cometruedream.tistory.com/247

 

[React] Tailwind ํ•œ์ค„, ๋‘์ค„, ์„ธ์ค„ ํšจ๊ณผ className ์ปค์Šคํ…€

๐Ÿคท‍โ™‚๏ธ Tailwind ํ•œ์ค„, ๋‘์ค„, ์„ธ์ค„ ํšจ๊ณผํ”„๋กœ์ ํŠธ ์ง„ํ–‰์ค‘ ๋ฐฐ๋„ˆ ๋งํฌ์— URL ๊ธธ์ด๊ฐ€ ๋„ˆ๋ฌด ๊ธธ์–ด์„œ 3์ค„ ํšจ๊ณผ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค. React + Tailwind CSS ์…‹ํŒ…๋œ ํ”„๋กœ์ ํŠธ ์—ฌ์„œ CSS ํŒŒ์ผ & style ํƒœ๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜

cometruedream.tistory.com

 

 

https://cometruedream.tistory.com/243

 

NestJS Lifecycle (์ƒ๋ช…์ฃผ๊ธฐ) ๊ฐœ๋…

๐Ÿคท‍โ™‚๏ธ ๋ณธ๋ก ์— ์•ž์„œ Nest JS ๊ฐœ๋…?Nest JS๋Š” Node JS ๊ธฐ๋ฐ˜์œผ๋กœ ํ•œ ์„œ๋ฒ„ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ”„๋ ˆ์ž„์›Œํฌ ์ž…๋‹ˆ๋‹ค. ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋Š” ์ƒ๋‹นํžˆ ์˜ค๋žซ๋™์•ˆ Express ๊ธฐ๋ฐ˜์œผ๋กœ ์„œ๋ฒ„ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐœ๋ฐœ์—์„œ ์••๋„์ ์ธ ์ 

cometruedream.tistory.com

 

 

https://cometruedream.tistory.com/242

 

๋ฆฌ์•กํŠธ Vite Proxy ํ™œ์šฉํ•œ CORS ์˜ค๋ฅ˜ ํ•ด๊ฒฐ

๐Ÿคท‍โ™‚๏ธ ๋ณธ๋ก ์— ์•ž์„œํ”„๋ก ํŠธ์—”๋“œ ๋ฒˆ๋“ค๋Ÿฌ ๊ฐœ๋…๊ณผ webpack vs vite ๋น„๊ต์— ๋Œ€ํ•ด์„œ ๊ณต๋ถ€๋ฅผ ํ•˜๊ณ  ๊ธ€์„ ๋ณด์‹œ๋ฉด ๋” ๋งŽ์€ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค^^ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์„ ์œ„ํ•œ ๋ฒˆ๋“ค๋Ÿฌ ๊ฐœ๋…๊ณผ webpack vs vite ๋น„๊ตhttps://comet

cometruedream.tistory.com

 

 

https://cometruedream.tistory.com/241

 

React ํด๋”๊ตฌ์กฐ FSD ๊ธฐ๋Šฅ ๋ถ„ํ™œ ์„ค๊ณ„ ์•„ํ‚คํ…์ฒ˜

๐Ÿคท‍โ™‚๏ธ ๋ณธ๋ก ์— ์•ž์„œํ”„๋กœ์ ํŠธ๋ฅผ ์‹œ์ž‘ํ•˜๊ธฐ์— ์•ž์„œ ํด๋” ๊ตฌ์กฐ, ์„ค๊ณ„์— ๋Œ€ํ•œ ์•„ํ‚คํ…์ฒ˜๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒƒ์€ ์ƒ๋‹นํžˆ ์–ด๋ ต๋‹ค. ์ด๋ฒˆ ํ”„๋กœ์ ํŠธ์— React FSD ํด๋”๊ตฌ์กฐ ์•„ํ‚คํ…์ฒ˜๋ฅผ ์„ค๊ณ„ํ•  ์˜ˆ์ •์ด๋ผ์„œ FSD ์•„ํ‚คํ…

cometruedream.tistory.com

 

 

https://cometruedream.tistory.com/244

 

์‰ฝ๊ฒŒ ์ดํ•ดํ•˜๊ณ  ์‚ฌ์šฉํ• ์ˆ˜ ์žˆ๋Š” Prisma ๋ช…๋ น์–ด ์ •๋ฆฌ

๐Ÿคท‍โ™‚๏ธ Prisma ๋ช…๋ น์–ด ๋ณธ๋ก ์— ์•ž์„œ Prisma๋ž€?Nest JS ํ”„๋ ˆ์ž„์›Œํฌ์™€ Prisma ORM์„ ํ™œ์šฉํ•ด์„œ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉด์„œ Prisma๋Š” Node JS ๊ธฐ๋ฐ˜์— ํ”„๋ ˆ์ž„์›Œํฌ/๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ์ƒํ˜ธ์ž‘์šฉ์ด ์ž˜๋˜๋ฉฐ Prisma + DB ์—ฐ๋™์œผ

cometruedream.tistory.com

 

 

https://itibiza.tistory.com/entry/%EB%8F%84%EC%BB%A4-%EC%9E%85%EB%AC%B8-%EC%8B%9C%EB%A6%AC%EC%A6%88-Dockerfile-%EA%B0%9C%EB%85%90-%EB%B0%8F-%EC%82%AC%EC%9A%A9%EB%B2%95

 

[๋„์ปค ์ž…๋ฌธ ์‹œ๋ฆฌ์ฆˆ] Dockerfile ๊ฐœ๋… ๋ฐ ์‚ฌ์šฉ๋ฒ•

์ด ๊ธ€์—์„œ๋Š” ์ž…๋ฌธ์ž๋ฅผ ์œ„ํ•œ ๋„์ปค ์‹œ๋ฆฌ์ฆˆ๋กœ ๋„์ปค ์ž…๋ฌธ ์‹œ๋ฆฌ์ฆˆ - Dockerfile ๊ฐœ๋… ๋ฐ ์‚ฌ์šฉ๋ฒ•์— ๋Œ€ํ•ด์„œ ์†Œ๊ฐœํ•ฉ๋‹ˆ๋‹ค. ์•ˆ๋…•ํ•˜์„ธ์š”! ITibiza ์ž…๋‹ˆ๋‹ค. ์ด์ „ ๊ธ€์—์„œ ๋„์ปค ์ด๋ฏธ์ง€, ์ปจํ…Œ์ด๋„ˆ์˜ ๊ฐœ๋…๊ณผ ๋„์ปค

itibiza.tistory.com