즐겨 사용하는 앱 중 일부가 실시간 업데이트를 어떻게 처리하는지 궁금한 적이 있나요? 실시간 스포츠 점수, 주식 시장 시세 표시기, 심지어 소셜 미디어 알림까지 모두 EDA(이벤트 중심 아키텍처)를 사용하여 데이터를 즉시 처리합니다. EDA는 모든 새로운 정보가 즉각적인 응답을 유발하는 대화와 같습니다. 이것이 바로 애플리케이션의 대화형 및 반응성을 높이는 것입니다.
이 연습에서는 Heroku에서 Apache Kafka를 사용하여 간단한 이벤트 기반 애플리케이션을 구축하는 과정을 안내합니다. 우리가 다룰 내용은 다음과 같습니다:
Apache Kafka 는 EDA 시스템 구축을 위한 강력한 도구입니다. 실시간 데이터 피드를 처리하도록 설계된 오픈 소스 플랫폼입니다. Heroku의 Apache Kafka는 Kafka를 서비스로 제공하는 Heroku 추가 기능입니다. Heroku를 사용하면 애플리케이션을 매우 쉽게 배포하고 관리할 수 있으며 최근 내 프로젝트에서 Heroku를 더 많이 사용하고 있습니다. Kafka를 Heroku와 결합하면 이벤트 기반 애플리케이션을 실행하려는 경우 설정 프로세스가 단순화됩니다.
이 가이드가 끝나면 Heroku에서 Apache Kafka를 사용하여 EDA의 강력한 기능을 보여주는 실행 중인 애플리케이션을 갖게 됩니다. 시작하자!
코드를 살펴보기 전에 몇 가지 핵심 개념을 빠르게 검토해 보겠습니다. 이 내용을 이해하고 나면 따라가기가 더 쉬워질 것입니다.
KafkaJS 라이브러리를 사용하여 Node.js 애플리케이션을 구축하겠습니다. 다음은 애플리케이션이 어떻게 작동하는지에 대한 간략한 개요입니다.
우리의 날씨 센서(생산자)는 온도, 습도, 기압과 같은 데이터를 주기적으로 생성하고 이러한 이벤트를 Apache Kafka로 보냅니다. 데모 목적으로 데이터는 무작위로 생성됩니다.
소비자가 주제를 듣게 될 것입니다. 새 이벤트가 수신되면 데이터가 로그에 기록됩니다.
전체 설정을 Heroku에 배포하고 Heroku 로그를 사용하여 이벤트가 발생할 때 이를 모니터링합니다.
시작하기 전에 다음 사항이 있는지 확인하세요.
이 전체 프로젝트의 코드베이스는 이 GitHub 저장소 에서 사용할 수 있습니다. 자유롭게 코드를 복제하고 이 게시물 전체를 따라해 보세요.
이제 기본 사항을 다루었으므로 Heroku에 Kafka 클러스터를 설정하고 구축을 시작하겠습니다.
Heroku에서 모든 것을 설정해 봅시다. 꽤 빠르고 쉬운 과정입니다.
~/project$ heroku login
~/project$ heroku create weather-eda
(내 Heroku 앱 이름을 weather-eda
로 지정했지만 앱의 고유한 이름을 선택할 수 있습니다.)
~/project$ heroku addons:create heroku-kafka:basic-0 Creating heroku-kafka:basic-0 on ⬢ weather-eda... ~$0.139/hour (max $100/month) The cluster should be available in a few minutes. Run `heroku kafka:wait` to wait until the cluster is ready. You can read more about managing Kafka at https://devcenter.heroku.com/articles/kafka-on-heroku#managing-kafka kafka-adjacent-07560 is being created in the background. The app will restart when complete... Use heroku addons:info kafka-adjacent-07560 to check creation progress Use heroku addons:docs heroku-kafka to view documentation
Heroku 추가 기능의 Apache Kafka에 대한 자세한 내용은 여기에서 확인할 수 있습니다. 데모에서는 추가 기능의 기본 0 계층을 추가합니다. 추가 기능 비용은 시간당 $0.139입니다. 이 데모 애플리케이션을 구축하면서 한 시간도 채 안 되는 시간 동안 추가 기능을 사용한 후 종료했습니다.
Heroku가 Kafka를 가동하고 준비하는 데 몇 분 정도 걸립니다. 곧 다음과 같은 내용을 보게 될 것입니다.
~/project$ heroku addons:info kafka-adjacent-07560 === kafka-adjacent-07560 Attachments: weather-eda::KAFKA Installed at: Mon May 27 2024 11:44:37 GMT-0700 (Mountain Standard Time) Max Price: $100/month Owning app: weather-eda Plan: heroku-kafka:basic-0 Price: ~$0.139/hour State: created
Kafka 클러스터가 가동되면 자격 증명과 기타 구성을 가져와야 합니다. Heroku는 애플리케이션에 대한 여러 구성 변수를 생성하여 방금 생성된 Kafka 클러스터의 정보로 채웁니다. 다음을 실행하여 이러한 구성 변수를 모두 볼 수 있습니다.
~/project$ heroku config === weather-eda Config Vars KAFKA_CLIENT_CERT: -----BEGIN CERTIFICATE----- MIIDQzCCAiugAwIBAgIBADANBgkqhkiG9w0BAQsFADAyMTAwLgYDVQQDDCdjYS1h ... -----END CERTIFICATE----- KAFKA_CLIENT_CERT_KEY: -----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAsgv1oBiF4Az/IQsepHSh5pceL0XLy0uEAokD7ety9J0PTjj3 ... -----END RSA PRIVATE KEY----- KAFKA_PREFIX: columbia-68051. KAFKA_TRUSTED_CERT: -----BEGIN CERTIFICATE----- MIIDfzCCAmegAwIBAgIBADANBgkqhkiG9w0BAQsFADAyMTAwLgYDVQQDDCdjYS1h ... F+f3juViDqm4eLCZBAdoK/DnI4fFrNH3YzhAPdhoHOa8wi4= -----END CERTIFICATE----- KAFKA_URL: kafka+ssl://ec2-18-233-140-74.compute-1.amazonaws.com:9096,kafka+ssl://ec2-18-208-61-56.compute-1.amazonaws.com:9096...kafka+ssl://ec2-34-203-24-91.compute-1.amazonaws.com:9096
보시다시피 몇 가지 구성 변수가 있습니다. 우리는 프로젝트 루트 폴더에 이러한 구성 변수 값이 모두 포함된 .env
라는 파일을 원할 것입니다. 이를 위해 다음 명령을 실행하면 됩니다.
~/project$ heroku config --shell > .env
.env
파일은 다음과 같습니다.
KAFKA_CLIENT_CERT="-----BEGIN CERTIFICATE----- ... -----END CERTIFICATE-----" KAFKA_CLIENT_CERT_KEY="-----BEGIN RSA PRIVATE KEY----- ... -----END RSA PRIVATE KEY-----" KAFKA_PREFIX="columbia-68051." KAFKA_TRUSTED_CERT="-----BEGIN CERTIFICATE----- ... -----END CERTIFICATE-----" KAFKA_URL="kafka+ssl://ec2-18-233-140-74.compute-1.amazonaws.com:9096,kafka+ssl://ec2-18-208-61-56.compute-1.amazonaws.com:9096...kafka+ssl://ec2-34-203-24-91.compute-1.amazonaws.com:9096"
또한 .gitignore 파일에 .env를 추가해야 합니다. 우리는 이 민감한 데이터를 저장소에 커밋하고 싶지 않습니다.
Heroku CLI는 기본적으로 Kafka 관련 명령을 제공하지 않습니다. Kafka를 사용하고 있으므로 CLI 플러그인을 설치 해야 합니다.
~/project$ heroku plugins:install heroku-kafka Installing plugin heroku-kafka... installed v2.12.0
이제 CLI에서 Kafka 클러스터를 관리할 수 있습니다.
~/project$ heroku kafka:info === KAFKA_URL Plan: heroku-kafka:basic-0 Status: available Version: 2.8.2 Created: 2024-05-27T18:44:38.023+00:00 Topics: [··········] 0 / 40 topics, see heroku kafka:topics Prefix: columbia-68051. Partitions: [··········] 0 / 240 partition replicas (partitions × replication factor) Messages: 0 messages/s Traffic: 0 bytes/s in / 0 bytes/s out Data Size: [··········] 0 bytes / 4.00 GB (0.00%) Add-on: kafka-adjacent-07560 ~/project$ heroku kafka:topics === Kafka Topics on KAFKA_URL No topics found on this Kafka cluster. Use heroku kafka:topics:create to create a topic (limit 40)
온전한 상태를 확인하기 위해 Kafka 클러스터를 사용해 보겠습니다. 주제를 만드는 것부터 시작합니다.
~/project$ heroku kafka:topics:create test-topic-01 Creating topic test-topic-01 with compaction disabled and retention time 1 day on kafka-adjacent-07560... done Use `heroku kafka:topics:info test-topic-01` to monitor your topic. Your topic is using the prefix columbia-68051.. ~/project$ heroku kafka:topics:info test-topic-01 ▸ topic test-topic-01 is not available yet
1분 정도 안에 우리의 주제를 사용할 수 있게 됩니다.
~/project$ heroku kafka:topics:info test-topic-01 === kafka-adjacent-07560 :: test-topic-01 Topic Prefix: columbia-68051. Producers: 0 messages/second (0 bytes/second) total Consumers: 0 bytes/second total Partitions: 8 partitions Replication Factor: 3 Compaction: Compaction is disabled for test-topic-01 Retention: 24 hours
다음으로, 이 터미널 창에서 우리는 소비자 역할을 하여 이 주제를 추적하여 듣습니다.
~/project$ heroku kafka:topics:tail test-topic-01
여기에서 터미널은 해당 주제에 게시된 이벤트를 기다립니다.
별도의 터미널 창에서 우리는 생산자 역할을 하고 해당 주제에 대한 일부 메시지를 게시할 것입니다.
~/project$ heroku kafka:topics:write test-topic-01 "hello world!"
소비자의 터미널 창으로 돌아가면 다음과 같은 내용이 표시됩니다.
~/project$ heroku kafka:topics:tail test-topic-01 test-topic-01 0 0 12 hello world!
훌륭한! Kafka 클러스터의 주제에 대한 이벤트를 성공적으로 생성하고 소비했습니다. 이제 Node.js 애플리케이션으로 넘어갈 준비가 되었습니다. 놀이터를 깔끔하게 유지하기 위해 이 테스트 주제를 파괴합시다.
~/project$ heroku kafka:topics:destroy test-topic-01 ▸ This command will affect the cluster: kafka-adjacent-07560, which is on weather-eda ▸ To proceed, type weather-eda or re-run this command with --confirm weather-eda > weather-eda Deleting topic test-topic-01... done Your topic has been marked for deletion, and will be removed from the cluster shortly ~/project$ heroku kafka:topics === Kafka Topics on KAFKA_URL No topics found on this Kafka cluster. Use heroku kafka:topics:create to create a topic (limit 40).
Kafka를 사용하기 위한 애플리케이션을 준비하려면 주제와 소비자 그룹이라는 두 가지를 생성해야 합니다.
애플리케이션이 사용할 주제를 만들어 보겠습니다.
~/project$ heroku kafka:topics:create weather-data
다음으로, 애플리케이션 소비자가 속하게 될 소비자 그룹을 만듭니다.
~/project$ heroku kafka:consumer-groups:create weather-consumers
Node.js 애플리케이션을 구축할 준비가 되었습니다!
새 프로젝트를 초기화하고 종속성을 설치해 보겠습니다.
~/project$ npm init -y ~/project$ npm install kafkajs dotenv @faker-js/faker pino pino-pretty
우리 프로젝트에는 두 가지 프로세스가 실행됩니다.
consumer.js
는 주제를 구독하고 게시된 모든 이벤트를 기록합니다.
producer.js
는 몇 초마다 해당 주제에 대한 무작위 날씨 데이터를 게시합니다.
이 두 프로세스 모두 KafkaJS를 사용하여 Kafka 클러스터에 연결해야 하므로 재사용이 가능하도록 코드를 모듈화하겠습니다.
프로젝트 src
폴더에 kafka.js
라는 파일을 생성합니다. 다음과 같습니다.
const { Kafka } = require('kafkajs'); const BROKER_URLS = process.env.KAFKA_URL.split(',').map(uri => uri.replace('kafka+ssl://','' )) const TOPIC = `${process.env.KAFKA_PREFIX}weather-data` const CONSUMER_GROUP = `${process.env.KAFKA_PREFIX}weather-consumers` const kafka = new Kafka({ clientId: 'weather-eda-app-nodejs-client', brokers: BROKER_URLS, ssl: { rejectUnauthorized: false, ca: process.env.KAFKA_TRUSTED_CERT, key: process.env.KAFKA_CLIENT_CERT_KEY, cert: process.env.KAFKA_CLIENT_CERT, }, }) const producer = async () => { const p = kafka.producer() await p.connect() return p; } const consumer = async () => { const c = kafka.consumer({ groupId: CONSUMER_GROUP, sessionTimeout: 30000 }) await c.connect() await c.subscribe({ topics: [TOPIC] }); return c; } module.exports = { producer, consumer, topic: TOPIC, groupId: CONSUMER_GROUP };
이 파일에서는 새로운 Kafka 클라이언트를 만드는 것부터 시작합니다. 이를 위해서는 Kafka 브로커에 대한 URL이 필요합니다. 이 URL은 .env
파일(원래 heroku 구성 호출에서 나온)의 KAFKA_URL
변수에서 구문 분석할 수 있습니다. 연결 시도를 인증하려면 KAFKA_TRUSTED_CERT
, KAFKA_CLIENT_CERT_KEY
및 KAFKA_CLIENT_CERT
제공해야 합니다.
그런 다음 Kafka 클라이언트에서 producer
와 consumer
생성하여 소비자가 weather-data
주제를 구독하도록 합니다.
kafka.js
에서는 주제와 소비자 그룹 이름 앞에 KAFKA_PREFIX
추가합니다. 우리는 다중 테넌트 Kafka 계획인 Heroku의 Apache Kafka용 Basic 0 계획을 사용하고 있습니다. 이는 KAFKA_PREFIX
사용하여 작업 한다는 의미입니다. 주제 이름을 weather-data
및 소비자 그룹 weather-consumers
라고 지정하더라도 다중 테넌트 Kafka 클러스터의 실제 이름 앞에는 KAFKA_PREFIX
추가되어야 합니다(고유함을 보장하기 위해).
따라서 기술적으로 데모의 경우 실제 주제 이름은 weather-data
columbia-68051.weather-data
입니다. (소비자 그룹 이름도 마찬가지입니다.)
이제 날씨 센서 생산자 역할을 할 백그라운드 프로세스를 만들어 보겠습니다. 프로젝트 루트 폴더에는 producer.js
라는 파일이 있습니다. 다음과 같습니다.
require('dotenv').config(); const kafka = require('./src/kafka.js'); const { faker } = require('@faker-js/faker'); const SENSORS = ['sensor01','sensor02','sensor03','sensor04','sensor05']; const MAX_DELAY_MS = 20000; const READINGS = ['temperature','humidity','barometric_pressure']; const MAX_TEMP = 130; const MIN_PRESSURE = 2910; const PRESSURE_RANGE = 160; const getRandom = (arr) => arr[faker.number.int(arr.length - 1)]; const getRandomReading = { temperature: () => faker.number.int(MAX_TEMP) + (faker.number.int(100) / 100), humidity: () => faker.number.int(100) / 100, barometric_pressure: () => (MIN_PRESSURE + faker.number.int(PRESSURE_RANGE)) / 100 }; const sleep = (ms) => { return new Promise((resolve) => { setTimeout(resolve, ms); }); }; (async () => { const producer = await kafka.producer() while(true) { const sensor = getRandom(SENSORS) const reading = getRandom(READINGS) const value = getRandomReading[reading]() const data = { reading, value } await producer.send({ topic: kafka.topic, messages: [{ key: sensor, value: JSON.stringify(data) }] }) await sleep(faker.number.int(MAX_DELAY_MS)) } })()
파일의 많은 코드는 임의의 값을 생성하는 것과 관련이 있습니다. 중요한 부분을 강조하겠습니다.
SENSORS
에서 찾을 수 있습니다.
temperature
, humidity
, barometric_pressure
등 세 가지 가능한 판독값 중 하나에 대한 값을 방출(게시)합니다. getRandomReading
객체에는 이러한 각 판독값에 대한 함수가 있어 합리적인 해당 값을 생성합니다.
while
루프가 있는 async
함수로 실행됩니다.
while
루프 내에서 우리는 다음을 수행합니다.
sensor
무작위로 선택하세요.reading
선택하십시오.value
생성합니다.producer.send
호출하세요. sensor
이벤트의 key
역할을 하며 reading
과 value
이벤트 메시지를 형성합니다. consumer.js
의 백그라운드 프로세스는 상당히 간단합니다.
require('dotenv').config(); const logger = require('./src/logger.js'); const kafka = require('./src/kafka.js'); (async () => { const consumer = await kafka.consumer() await consumer.run({ eachMessage: async ({ topic, partition, message }) => { const sensorId = message.key.toString() const messageObj = JSON.parse(message.value.toString()) const logMessage = { sensorId } logMessage[messageObj.reading] = messageObj.value logger.info(logMessage) } }) })()
우리 consumer
이미 weather-data
주제를 구독하고 있습니다. consumer.run
호출한 다음 eachMessage
에 대한 핸들러를 설정합니다. Kafka는 consumer
에게 메시지를 알릴 때마다 메시지를 기록합니다. 그게 전부입니다.
Procfile
package.json
파일에서 생산자 및 소비자 백그라운드 프로세스를 시작하는 몇 가지 scripts
추가해야 합니다. 이제 파일에는 다음이 포함되어야 합니다.
... "scripts": { "start": "echo 'do nothing'", "start:consumer": "node consumer.js", "start:producer": "node producer.js" }, ...
중요한 것은 start:consumer
및 start:producer
입니다. 그러나 Heroku 빌더는 파일이 있을 것으로 예상하기 때문에 파일에서 start
유지합니다(의미 있는 작업은 아니지만).
다음으로 Heroku 앱에 필요한 다양한 작업자를 시작하는 방법을 Heroku에 알려주는 Procfile
을 만듭니다. 프로젝트의 루트 폴더에서 Procfile
다음과 같아야 합니다.
consumer_worker: npm run start:consumer producer_worker: npm run start:producer
아주 간단하죠? consumer_worker
라는 백그라운드 프로세스 작업자와 producer_worker
라는 또 다른 작업자가 있습니다. 웹 애플리케이션용 Procfile
에서 일반적으로 볼 수 있는 web
작업자가 없다는 것을 알 수 있습니다. Heroku 앱의 경우 두 명의 백그라운드 작업자만 있으면 됩니다. 우리는 web
필요하지 않습니다.
이로써 모든 코드가 설정되었습니다. 모든 코드를 리포지토리에 커밋했으며 배포할 준비가 되었습니다.
~/project$ git push heroku main … remote: -----> Build succeeded! … remote: -----> Compressing... remote: Done: 48.6M remote: -----> Launching... … remote: Verifying deploy... done
배포한 후에는 다이노의 크기를 적절하게 조정하고 싶습니다. 웹 프로세스에는 dyno가 필요하지 않지만, consumer_worker
및 producer_worker
모두에는 dyno가 필요합니다. 우리는 필요에 따라 이러한 프로세스를 설정하기 위해 다음 명령을 실행합니다.
~/project$ heroku ps:scale web=0 consumer_worker=1 producer_worker=1 Scaling dynos... done, now running producer_worker at 1:Eco, consumer_worker at 1:Eco, web at 0:Eco
이제 모든 것이 준비되어 실행되어야 합니다. 뒤에서 producer_worker
는 Kafka 클러스터에 연결한 다음 몇 초마다 날씨 센서 데이터 게시를 시작해야 합니다. 그런 다음 consumer_worker
는 Kafka 클러스터에 연결하고 구독한 주제에서 수신하는 모든 메시지를 기록해야 합니다.
consumer_worker
무엇을 하고 있는지 보려면 Heroku 로그를 보면 됩니다.
~/project$ heroku logs --tail … heroku[producer_worker.1]: Starting process with command `npm run start:producer` heroku[producer_worker.1]: State changed from starting to up app[producer_worker.1]: app[producer_worker.1]: > [email protected] start:producer app[producer_worker.1]: > node producer.js app[producer_worker.1]: … heroku[consumer_worker.1]: Starting process with command `npm run start:consumer` heroku[consumer_worker.1]: State changed from starting to up app[consumer_worker.1]: app[consumer_worker.1]: > [email protected] start:consumer app[consumer_worker.1]: > node consumer.js app[consumer_worker.1]: app[consumer_worker.1]: {"level":"INFO","timestamp":"2024-05-28T02:31:20.660Z","logger":"kafkajs","message":"[Consumer] Starting","groupId":"columbia-68051.weather-consumers"} app[consumer_worker.1]: {"level":"INFO","timestamp":"2024-05-28T02:31:23.702Z","logger":"kafkajs","message":"[ConsumerGroup] Consumer has joined the group","groupId":"columbia-68051.weather-consumers","memberId":"weather-eda-app-nodejs-client-3ee5d1fa-eba9-4b59-826c-d3b924a6e4e4","leaderId":"weather-eda-app-nodejs-client-3ee5d1fa-eba9-4b59-826c-d3b924a6e4e4","isLeader":true,"memberAssignment":{"columbia-68051.test-topic-1":[0,1,2,3,4,5,6,7]},"groupProtocol":"RoundRobinAssigner","duration":3041} app[consumer_worker.1]: [2024-05-28 02:31:23.755 +0000] INFO (21): {"sensorId":"sensor01","temperature":87.84} app[consumer_worker.1]: [2024-05-28 02:31:23.764 +0000] INFO (21): {"sensorId":"sensor01","humidity":0.3} app[consumer_worker.1]: [2024-05-28 02:31:23.777 +0000] INFO (21): {"sensorId":"sensor03","temperature":22.11} app[consumer_worker.1]: [2024-05-28 02:31:37.773 +0000] INFO (21): {"sensorId":"sensor01","barometric_pressure":29.71} app[consumer_worker.1]: [2024-05-28 02:31:54.495 +0000] INFO (21): {"sensorId":"sensor05","barometric_pressure":29.55} app[consumer_worker.1]: [2024-05-28 02:32:02.629 +0000] INFO (21): {"sensorId":"sensor04","temperature":90.58} app[consumer_worker.1]: [2024-05-28 02:32:03.995 +0000] INFO (21): {"sensorId":"sensor02","barometric_pressure":29.25} app[consumer_worker.1]: [2024-05-28 02:32:12.688 +0000] INFO (21): {"sensorId":"sensor04","humidity":0.1} app[consumer_worker.1]: [2024-05-28 02:32:32.127 +0000] INFO (21): {"sensorId":"sensor01","humidity":0.34} app[consumer_worker.1]: [2024-05-28 02:32:32.851 +0000] INFO (21): {"sensorId":"sensor02","humidity":0.61} app[consumer_worker.1]: [2024-05-28 02:32:37.200 +0000] INFO (21): {"sensorId":"sensor01","barometric_pressure":30.36} app[consumer_worker.1]: [2024-05-28 02:32:50.388 +0000] INFO (21): {"sensorId":"sensor03","temperature":104.55}
효과가있다! 소비자가 메시지를 수신하고 기록하기 때문에 생산자가 주기적으로 Kafka에 메시지를 게시하고 있다는 것을 알고 있습니다.
물론 대규모 EDA 앱에서는 모든 센서가 생산자입니다. 다양한 목적으로 여러 주제에 대해 게시할 수도 있고, 모두 동일한 주제에 대해 게시할 수도 있습니다. 그리고 소비자는 여러 주제를 구독할 수 있습니다. 또한 데모 앱에서 소비자는 eachMessage
에 대해 많은 정보를 내보냈습니다. 그러나 EDA 애플리케이션에서는 소비자가 타사 API를 호출하거나 SMS 알림을 보내거나 데이터베이스를 쿼리하여 응답할 수 있습니다.
이제 이벤트, 주제, 생산자, 소비자에 대한 기본적인 이해를 갖추고 Kafka로 작업하는 방법을 알았으므로 더 복잡한 비즈니스 사용 사례를 충족하기 위해 고유한 EDA 애플리케이션을 설계하고 구축할 수 있습니다.
EDA는 매우 강력합니다. 손쉬운 확장성 및 실시간 데이터 처리와 같은 주요 기능을 즐기면서 시스템을 분리할 수 있습니다. EDA의 경우 Kafka는 처리량이 높은 데이터 스트림을 쉽게 처리하는 데 도움이 되는 핵심 도구입니다. Heroku에서 Apache Kafka를 사용하면 빠르게 시작할 수 있습니다. 관리형 서비스이므로 Kafka 클러스터 관리의 복잡한 부분에 대해 걱정할 필요가 없습니다. 앱 구축에만 집중할 수 있습니다.
이제부터 실험하고 프로토타입을 제작할 시간입니다. EDA에 적합한 사용 사례를 식별합니다. Heroku에서 직접 테스트하고 놀라운 것을 만들어보세요. 즐거운 코딩하세요!