배경 CS 처리량을 줄이기 위한 챗봇 프로젝트의 책임자가 되었다. 챗봇이 답할 근거는 FAQ 데이터였고, FAQ가 바뀔 때마다 임베딩해서 우리 DB에 실시간으로 반영하는 것 이 목표였다. 왜 그냥 처리하면 안 되는가 임베딩 1건 5초 , 일 최대 1,600건 → 총 작업량 8,000초(약 2시간 13분) API Gateway 통합 타임아웃 기본 29초 , Lambda 최대 실행 900초(15분) 즉 요청 안에서 처리하면 타임아웃 때문에 동작 자체가 불가능 문제는 "느리다"가 아니라 동기 실행이라는 구조 였다. 아키텍처 FAQ 변경 → API Gateway 주소로 webhook 호출 변경 데이터를 DB에 저장하고 SQS 로 전달 후 즉시 리턴 임베딩은 병렬 가능하므로 최대 100개 람다가 동시에 소비해 벡터 생성 및 DB 적재 처리 실패는 DLQ 로 격리, 무한 재시도 없이 담당자에게 알람 요청 경로에서 8,000초를 떼어낸 것이 핵심이다. webhook 응답은 임베딩 시간과 무관해진다. 왜 100개인가 — 병목은 임베딩이 아니라 DB다 동시성을 올리면 임베딩은 빨라지지만, 적재를 위해 DB 커넥션을 잡는 순간 병목이 DB로 옮겨간다. DB 최대 커넥션 500개 다른 서버 사용분을 제외하고 남은 몫의 1/3 을 임베딩 람다에 할당 결과: 최대 동시성 100 100은 임베딩 속도가 아니라 DB 커넥션 예산에서 역산한 값 이다. 워커 수는 가장 좁은 공유 자원에서 결정된다. 에러 처리 webhook 실패 — API 단에서 HTTP 상태 코드를 리턴해 즉시 실패를 알린다 접수 후 임베딩 실패 — DLQ로 넘기고 담당자에게 알람. 무한 재시도 대신 격리한다 트레이드오프 최종 일관성 : 응답 시점엔 임베딩이 없다. 변경이 몰리면 최대 80초 늦게 검색에 반영된다 → "실시간에 가까운"이 정확한 표현 멱등성 : SQS는 at-least-once이므로 재처리에 무해하도록 upsert 설계 정리 느린 작업은 요청 경로에서 떼어낸다 (접수/처리 분리) SQS로 부하를 평탄화하고, 동시 소비자 수로 성능을 조절 한다 동시성은 임베딩 속도가 아니라 DB 커넥션에서 역산 한다 → 100 대가는 최종 일관성이며, 지표로 통제한다
Le prix d’un référencement Google en agence dépend du site, de la concurrence et du travail réellement inclus dans la prestation. Pour préparer un budget en 2027, une petite entreprise peut envisager plusieurs centaines d’euros par mois pour une mission locale limitée, tandis qu’un accompagnement plus complet peut nécessiter plusieurs milliers d’euros mensuels. Ces montants constituent des repères de préparation budgétaire, pas une grille officielle pour 2027. Le tarif définitif doit être confirmé par un devis précisant les livrables, les frais supplémentaires et la durée de l’engagement. La question décisive reste simple : que réalise concrètement l’agence pour le montant demandé ? Que signifie « référencement Google » dans un devis ? Cette expression peut désigner deux prestations différentes. Le référencement naturel, ou SEO , consiste à améliorer les pages, les contenus et le fonctionnement d’un site pour développer sa visibilité dans les résultats non publicitaires de Google. Le référencement payant, ou Google Ads , consiste à diffuser des annonces avec un budget publicitaire. Dans ce cas, les honoraires de l’agence et les dépenses publicitaires sont deux postes distincts. Avant de comparer les tarifs, vérifiez donc ce que couvre chaque proposition. Un forfait SEO mensuel et une gestion de campagnes publicitaires ne financent pas le même travail. Cet article porte principalement sur le référencement naturel. Quel budget prévoir pour une agence SEO en 2027 ? Pour construire une première enveloppe, vous pouvez utiliser les scénarios indicatifs suivants. Ils ne constituent ni une moyenne nationale ni une promesse de résultats. Une petite entreprise avec un objectif local Une enveloppe de 500 à 1 000 € HT par mois peut servir de point de départ pour une mission ciblée sur une activité, une zone géographique et un site de taille limitée. Le périmètre peut comprendre : L’optimisation des principales pages de services. L’amélioration de la présence locale de l’entreprise. La correction de certains problèmes techniques. Le suivi des demandes de contact. Des recommandations éditoriales prioritaires. À ce niveau de budget, il faut définir des priorités. La création de nombreux contenus, une refonte du site ou des interventions complexes peuvent nécessiter un devis séparé. Une PME qui souhaite développer sa visibilité nationale Une enveloppe de 1 000 à 3 000 € HT par mois peut être envisagée pour un accompagnement plus étendu. Le travail peut porter sur plusieurs catégories de services, des contenus commerciaux, le maillage interne, les corrections techniques et le suivi des conversions. Le montant doit correspondre à une capacité de production identifiable. Une réunion mensuelle et un rapport automatisé ne représentent pas le même investissement qu’une prestation comprenant rédaction, intégration et corrections. Un site e-commerce ou un secteur très concurrentiel Pour préparer le budget d’un projet plus exigeant, une enveloppe de 3 000 à 6 000 € HT par mois, voire davantage , peut être nécessaire selon le périmètre retenu. Un catalogue important peut demander un travail sur les catégories, les filtres, les variantes, les pages produits et les contenus dupliqués. Une stratégie internationale ajoute aussi des besoins de localisation et de coordination. La taille du site ne suffit toutefois pas à déterminer le prix. Un petit site sur un marché très concurrentiel peut demander davantage de travail qu’un catalogue plus large positionné sur une niche. Combien coûte un audit SEO ? Un audit SEO est une prestation ponctuelle destinée à identifier les obstacles, les possibilités de progression et les actions prioritaires. Pour préparer un budget, une entreprise peut réserver environ 1 000 à 3 000 € HT pour un audit de site limité , avec une enveloppe supérieure pour un site complexe, multilingue ou comportant un catalogue important. Le devis doit préciser la profondeur de l’analyse. Un audit utile doit permettre de comprendre : Quels problèmes freinent la visibilité. Quelles pages ont un potentiel commercial. Quelles corrections effectuer en premier. Qui doit réaliser chaque intervention. Comment vérifier que les changements fonctionnent. Le prix de l’audit n’inclut pas nécessairement la mise en œuvre. Si l’agence remet uniquement des recommandations, vous devrez aussi prévoir le temps ou le budget nécessaire pour les appliquer. Pourquoi deux agences proposent-elles des prix différents ? Un écart de tarif peut refléter une différence de périmètre, d’expérience, de production ou d’organisation. L’état initial du site Un site correctement structuré demande moins de remise en ordre qu’un site présentant des problèmes d’exploration, des pages mal organisées ou une migration mal préparée. L’agence doit expliquer ce qui relève de la correction initiale et ce qui relève du travail récurrent. La concurrence sur les recherches visées Se positionner sur une recherche locale précise ne représente pas le même chantier que développer une visibilité nationale sur un secteur disputé. Le devis doit relier les objectifs à une analyse du marché. Un budget proposé sans examen du site ni des concurrents mérite des explications supplémentaires. La production de contenus Certaines agences fournissent seulement des sujets et des consignes. D’autres prennent en charge la rédaction, la validation, l’intégration et les mises à jour. Demandez ce qui est inclus, notamment lorsque vos contenus nécessitent une expertise métier ou une vérification interne. Les interventions techniques Une agence peut identifier les corrections sans disposer des accès ou des compétences nécessaires pour les réaliser directement. Vérifiez qui intervient sur le site, comment le travail est validé et si le développement est facturé séparément. Le niveau d’accompagnement La disponibilité d’un interlocuteur, la fréquence des échanges et la coordination avec vos équipes ont un coût. Cet accompagnement doit faciliter les décisions et l’exécution. Il doit aussi laisser suffisamment de budget pour le travail qui améliore effectivement le site. Que doit contenir un forfait mensuel ? Un forfait clair décrit des actions, des responsabilités et des livrables. Il peut comprendre une analyse initiale, une feuille de route, des optimisations de pages, une production éditoriale, des corrections techniques et un suivi des résultats. Pour chaque poste, demandez des précisions : Combien de pages seront travaillées ? Quels contenus seront produits ? Qui les intégrera sur le site ? Quelles corrections seront effectivement réalisées ? Quels indicateurs seront suivis ? Quels éléments seront facturés en supplément ? Une formule comme « optimisation continue » reste insuffisante si elle ne permet pas de comprendre ce qui sera fait pendant les premiers mois. Quels frais supplémentaires faut-il anticiper ? Le tarif mensuel affiché peut ne représenter qu’une partie du budget total. Les frais de démarrage L’audit, la configuration du suivi et la préparation de la stratégie peuvent être facturés au lancement. Demandez si ces frais s’ajoutent au premier mois ou s’ils sont inclus dans l’engagement. Le développement du site Une modification de navigation, une correction de thème ou une intervention sur les filtres peut nécessiter un développeur. Ces dépenses doivent être identifiées avant de valider la stratégie. Les contenus spécialisés Des photographies, des traductions, des illustrations ou des textes nécessitant une expertise particulière peuvent être exclus du forfait. Les actions de notoriété Si une proposition comprend des partenariats éditoriaux ou des actions destinées à obtenir des mentions, demandez le détail du budget et des méthodes employées. L’achat de liens destiné à manipuler les classements présente un risque. Une agence doit pouvoir expliquer ses pratiques et leurs implications. Le temps de votre équipe La validation des contenus, les informations sur les produits et les échanges techniques mobilisent vos collaborateurs. Un projet peut prendre du retard si personne n’est disponible pour répondre aux questions ou autoriser les modifications. Comment calculer le coût annuel réel ? Le budget annuel se calcule en additionnant les honoraires récurrents, les frais de lancement et les prestations complémentaires prévues. Prenons un exemple fictif de planification : Accompagnement SEO : 1 500 € HT par mois pendant douze mois. Audit initial : 2 000 € HT. Corrections techniques : 2 500 € HT. Production complémentaire : 1 500 € HT. Le total prévisionnel atteint 24 000 € HT sur l’année . Cet exemple montre pourquoi il faut comparer le coût global. Une offre mensuelle moins chère peut finalement coûter davantage si elle exclut des interventions indispensables. Vérifiez également les conditions de paiement, de renouvellement et de résiliation. Comment savoir si le référencement peut être rentable ? La rentabilité dépend de la valeur des clients acquis et de votre capacité à transformer les visites en ventes ou en demandes qualifiées. Pour un commerce en ligne, examinez la marge générée par les commandes supplémentaires. Pour une entreprise de services, suivez les prospects qualifiés, les ventes conclues et la marge correspondante. Voici un exemple fictif : Une entreprise dépense 2 000 € par mois en référencement. Chaque nouveau client apporte 500 € de marge contributive, après les coûts directement liés à la vente. Il faut quatre clients supplémentaires pour couvrir ces seuls honoraires. Ce calcul ne prouve pas que le SEO générera ces clients. Il aide à déterminer le résultat commercial nécessaire pour justifier l’investissement. Il faut aussi tenir compte du décalage entre les dépenses et les effets obtenus. Une dépense mensuelle ne produit pas automatiquement un retour immédiat. Quels résultats demander à l’agence ? Les positions et les visites sont utiles pour suivre la visibilité, mais elles doivent être reliées à votre activité. Les indicateurs peuvent inclure : Les clics vers les pages priorita
안녕하세요, 오랜만에 글을 작성해요. 취업을 목표로 개인 프로젝트 및 팀 프로젝트를 진행하고 이를 바탕으로 이력서 및 포폴을 작성했는데요. 여러 면접 기회가 있었지만 아쉽게도 현재까지 최종 합격을 하지 못했습니다. 순수하게 말 못하는 청년이라서 아쉽습니다. ᅮᅮ 그래도 포기하지 않고 노력해보겠습니다. 이젠 꾸준히 글을 작성하도록 할 예정입니다. 다음에 봬요~.
문제 상황: 계정 A의 프리티어 기간이 종료되었다. 그래서 다른 프리티어 계정 B로 서버를 이전하고자한다. 해결 방법: 계정 A에서 계정 B로 EC2를 옮기자. 현재 서버 구조: RDS 비용 절감을 위해 8월에 이미 RDS를 삭제하고, ec2에 mysql을 설치해서 DB 데이터를 이전하였다. 따라서 현재 하나의 ec2 서버 위에 spring, mysql, ngnix가 모두 한꺼번에 있는 구조이다. (최소한의 비용을 위해) 1. AWS 서버 이전 AWS EC2를 이전하는 가장 간단한 방법은 AWS에서 제공해주는 AMI로 복제하는 것이다. AMI(Amazon Machine Images)는 EC2 인스턴스를 설정하고 부팅하는데 필요한 소프트웨어를 제공하는 이미지이다. AMI 이미지로 EC2 인스턴스를 만들면 동일한 세팅으로 EC2를 여러 개 만들 수 있기 때문에 서버 이전이나 복제에 주로 사용된다. 이때는 리전, 운영체제 등의 조건이 같아야한다. 출처: AWS 공식 문서 - Amazon Machine Images in Amazon EC2 2. 계정 A : 서버 이미지 생성 및 공유 2.1 기존 EC2의 AMI 생성 1) AMI 생성 기존 계정 A에서 AMI를 생성한다. EC2 → 인스턴스 → 기존 인스턴스 선택 → 작업 → 이미지 및 템플릿 → 이미지 생성 2) 옵션 선택 DB가 같은 EC2에서 실행 중이라면 기본 재부팅 방식으로 생성하는 것이 안전하다. No reboot은 파일시스템 정합성이 보장되지 않을 수 있어서 재부팅 항목도 체크해주었다. AMI 이름 입력하기 가능하면 재부팅 옵션 선택 3) 생성 완료 그러면 AMI 이미지가 생성된다. EC2 → AMI에서 Available 상태 확인하면 최종 생성이 된것이다. 이제 계정 A에서 ec2의 AMI 생성이 끝났다. 4) AMI 복사 후 서버 체크 (추가) AMI 복사 후 인스턴스가 재부팅되면서 원래 서버가 제대로 올라왔는지 꼭 확인한다. 만약 서버가 내려갔으면 우선 수동 실행으로라도 서버를 동작 시켜둔다. 나도 아래와 같이 오류가 발생해서 서버가 잠깐 내려갔었다. [트러블 슈팅] 문제 상황: 인스턴스 재부팅으로 deploy.sh가 실행됐는데, 로그 쓰는 동작 권한이 막혀서 배포에 실패하고 운영서버가 내려갔다. 해결 방법 : 아래와 같이 권한을 ubuntu로 바꿔서 해결했다. $ sudo chown ubuntu:ubuntu \ /home/ubuntu/backend/app/app.log \ /home/ubuntu/backend/app/error.log 1.2 AMI 비공개 공유 1) 권한 편집 계정 A에서 계정 B로 AMI 비공개 공유해야한다. 이를 위해 계정 A에서 방금 생성한 AMI 권한을 편집한다. EC2 → AMI → 생성한 AMI 선택 → 작업 → AMI 권한 편집 AMI 가용성을 비공개(Private)로 유지 공유 계정에 계정 B의 12자리 계정 ID 추가 공개 AMI로 바꾸지 말고 특정 계정에만 공유하도록 한다.❗️ 출처: AWS 공식 문서 - AWS 계정 간 AMI 공유 방법 2) 공유 id 입력 AMI 공유 칸에 계정 B의 id를 넣는다. 계정 B의 AWS 계정 ID 확인 B 계정에서 오른쪽 위 계정 메뉴를 누르면 12자리 AWS 계정 ID를 확인할 수 있다. ex. 123456789012 ❗️ IAM 사용자 ID가 아니라 AWS 계정 ID가 필요하다. * 3) 공유 확인 * 아래와 같이 AMI 공유 계정에 계정 B가 추가되면, 계정 B에서 해당 AMI를 다운받을 수 있다. 이제 계정 A의 AMI를 계정 B로 공유가 완료되었다. 2. 계정 B : AMI 복사 및 인스턴스 생성 2.1 공유된 AMI를 복사 1) AMI 확인 이제 계정 B로 로그인하고, 기존 AMI와 같은 리전 선택한다. 아까 프라이빗으로 공유했으니, 필터에서 프라이빗 이미지를 누르면 계정 A에서 공유한 AMI 이미지가 뜬다. EC2 → AMI → Private images → 계정 A에서 공유한 AMI 확인 * 2) AMI 복사 * 공유받은 AMI를 그대로 사용할 수도 있지만, 계정 A와 완전히 분리하려면 계정 B에서 복사하는 것이 좋다. 공유된 AMI 선택 → 작업 → AMI 복사 3) AMI 복사 옵션 선택 AMI는 리전 단위이므로 양쪽 콘솔의 리전이 서울이라면 모두 ap-northeast-2여한다. 대상 리전 선택 새 AMI 이름 입력 [트러블 슈팅] 문제: 계정 B에서 AMI 접근 권한이 없어서 발생한 오류이다. 해결: 계정 A에서 공유 계정에 계정 B의 id가 제대로 들어갔는지 확인하고, 없다면 추가한다. EC2 → AMI → AMI 선택 → 스토리지 → 스냅샷 선택 → 권한 수정 → 계정 B의 id 추가 4) 복사 확인 복사를 실행하고 복사본이 Available이 될 때까지 기다린다. 사용 가능이 뜨면 성공이다! 계정 B가 소유한 독립적인 AMI와 EBS 스냅샷이 만들어졌기 때문에, 이후 계정 A에서 공유를 해제하거나 원본 AMI를 삭제해도 계정 B의 복사본은 유지된다. 이제 계정 A의 AMI를 복사한 계정 B의 AMI가 생성이 끝났다. 스냅샷 저장 비용 나는 완료기간이 없는 스냅샷이기 때문에 스냅샷 저장 비용이 들지 않지만, 그래도 혹시 몰라서 이전 이후 스냅샷을 삭제하였다. *시간 기반 스냅샷 복사에 대해서는 비용이 발생할 수 있다. - AWS AMI 복사 문서 2.2 복사한 AMI로 인스턴스 생성 이제 계정 B에서 복사한 AMI로 EC2 인스턴스를 생성해야한다. AMI를 선택하고 인스턴스 시작을 누른다. EC2 인스턴스 생성할 때와 동일하게 해주면된다. 이미지에서는 복사를 뜬 AMI를 선택해준다. 키페어를 생성해서 다운 받고, 보안 그룹도 설정해준다. 이때, 개발 편의성을 위한다면 0.0.0.0/0을 사용하지만, 보안을 위한다면 내 ip 만 허용하도록 한다. 스토리지 구성까지 마치면 인스턴스를 생성할 수 있다. 3. 계정 B: EC2 환경 세팅 이제 인스턴스 복제는 끝났지만, 계정 B의 환경에 맞춰서 변경해줘야하는 값들이 있다. 3.1 env의 값 변경 AMI 복제를 했기 때문에 이전 계정 A에서의 환경변수 값이 그대로 일 것이다. 따라서, 계정 B EC2에 .env 파일에 있는 환경변수 값을 바뀐 환경에 맞게 변경해줘야한다. $ sudo nano .env $ source .env 3.2 ngnix 및 https 설정 https 인증서를 재발급 받고, ngnix도 맞춰서 수정해줘야한다. 지금 port switching 방법을 사용하기 때문에, 포트 번호 8081과 8082를 번갈아가면서 사용하고 있다. Nginx 설정을 배포할 때마다 config에서 포트 번호를 직접 수정하기보다는, upstream 설정을 별도 파일로 분리해서 링크를 전환하는 방식이 깔끔하다. 1) 포트별 config 파일 생성 먼저 설정파일을 따로 생성해준다. $ sudo mkdir -p /etc/nginx/upstreams 포트 8081번 config $ sudo tee /etc/nginx/upstreams/zzicgo-8081.conf > /dev/null <<'EOF' upstream zzicgo_backend { server 127.0.0.1:8081; keepalive 32; } EOF 포트 8082번 config $ sudo tee /etc/nginx/upstreams/zzicgo-8082.conf > /dev/null <<'EOF' upstream zzicgo_backend { server 127.0.0.1:8082; keepalive 32; } EOF 2) config 적용 지금 8081 포트에서 실행중이라면 해당 config를 적용하도록 아래의 명령어를 입력한다. $ sudo ln -sfn \ /etc/nginx/upstreams/zzicgo-8081.conf \ /etc/nginx/conf.d/zzicgo-upstream.conf 3) ngnix 설정 수정 핵심은 ngnix 설정에서 proxy_pass를 특정 포트에 고정하지 않고 ringout_backend라는 upstream 이름을 사용하며, Health Check 성공 후 upstream 링크를 8081과 8082 사이에서 전환하는 것이다. 1. Nginx → 8081 2. 새 Spring을 8082에서 실행 3. 8082 Health Check 성공 4. upstream 링크를 8082 설정으로 변경 5. nginx -t 6. Nginx reload 7. 기존 8081 Spring 종료 ngnix 설정 파일에서 proxy_pass 에 http://zzicgo_backend 로 수정한다. server { listen 80 default_server; listen [::]:80 default_server; server_name _; location / { proxy_pass http://zzicgo_backend; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } 현재 Config 확인 명령어 아래 명령어로 현재 읽히고 있는 config를 확인할 수 있다. $ readlink -f /etc/nginx/conf.d/zzicgo-upstream.conf 결과로 "/etc/nginx/upstreams/zzicgo-8081.conf"로 나온다면 8081 config를 읽고 있는것이다. 4) ngnix 변경 반영 ngnix 변경을 반영하고 서버가 연결되었는지 확인한다. # Nginx 문법 검사 $ sudo nginx -t # 변경 사항 반영 $ sudo systemctl reload nginx # 서버로 연결되는지 확인 $ curl -IL http://api.example.com/swagger-ui.html 5) Https 인증서 발급 도 허용 시켜둔다. 두 도메인에 HTTPS 인증서를 발급하고, Nginx에 자동으로 적용하는 명령어이다. sudo certbot --nginx \ -d zzicgo.com \ -d api.zzicgo.com 3.3 deploy.sh 수정** 배포 파일에서도 Spring 실행 옵션에 --server.address=127.0.0.1을 추가한다. 이를 통해 Spring은 EC2 내부의 127.0.0.1:8081 또는 127.0.0.1:8082에서만 요청을 받는다. 기존에는 외부에서 http://{EC2_IP}:{포트}/swagger-ui/index.html로 Spring에 직접 접근할 수 있었지만, 변경 후에는 8081·8082 포트로 직접 접근할 수 없다. 외부 요청은 DNS를 통해 EC2의 Nginx로 전달되고, Nginx가 현재 활성화된 Spring 포트로 요청을 프록시한다. 따라서 사용자는 https://{도메인}/swagger-ui/index.html로 접속하게 된다. AWS EC2 인바운드 규칙 수정 (추가) 추가로 EC2 보안 그룹의 인바운드 규칙에서도 8081·8082 포트를 제거하여, 외부에서 Spring에 직접 접근하지 못하도록 한다. Spring은 127.0.0.1:8081 또는 127.0.0.1:8082에서 실행되기 때문에 EC2 내부에서는 Health Check가 가능하지만, 외부 요청은 80·443 포트로 Nginx에 들어온 뒤 현재 활성화된 Spring 포트로 전달된다. 이를 통해 외부에서는 Spring에 직접 접근하지 않고 Nginx를 통해서만 접근하도록 제한한다. 4. S3 버킷 이전하기 기존 프로젝트에서 S3를 사용했었다. AMI로는 S3가 복사되지 않기 때문에, 계정 B에서 새로 S3를 만들고 데이터를 옮겨와야한다. 1) 계정 B에서 새로운 S3 버킷 생성 2) 계정 A의 데이터를 계정 B로 이전 추가로 계정 A에 있던 S3 데이터를 계정 B의 S3로 그대로 옮기기 위해서는 아래와 같은 과정을 거칠것이다. 계정 A 기존 버킷 → 계정 B EC2의 IAM Role에 읽기 권한 부여 → 계정 B 신규 버킷으로 sync → Spring의 버킷명 변경 → 최종 전환 직전에 한 번 더 sync 4.1 계정 B: S3 버킷 생성 1) S3 버킷 생성 [ZzicGo] AWS S3 사용하여 사진 업로드 기능 구현 (Spring) 를 참고해서 그대로 만들면 된다. 이때는 그냥 네임스페이스를 바로 입력했는데, 지금은 "계정 리전 네임스페이스"라는 선택지가 생겼다. AWS 공식 문서 - 범용 버킷의 네임스페이스 계정 리전 네임스페이스는 "접두사 + 계정 ID + 리전 + -an" 조합으로 S3 버킷의 이름을 생성하기 때문에 다른 계정이 동일한 이름으로 버킷 생성이 불가능하다. 따라서 AWS에서는 이 방식을 권장하고 있다. S3 버킷 정책 Public 버킷 정책에 아래처럼 public 읽기 허용을 넣고, 퍼블릭 액세스 차단도 해제해주면 aws s3에서 객체 URL로 바로 접근이 가능하다. { "Version": "2012-10-17", "Statement": [ { "Sid": "PublicReadProfileImages", "Effect": "Allow", "Principal": "*", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::{❗️계정-B-버킷명}/profile/*" } ] } 2) S3 IAM 사용자 생성 ZzicGo사진-업로드-화면-S3-연동#iam-사용자 를 참고해서 계정 B에서도 iam 사용자를 만든다. 그리고 그 사용자의 액세스 키값을 EC2 .env에 갱신하는 거까지 완료하면 된다. # 예시: AWS_IAM_ACCESS_KEY=... AWS_IAM_SECRET_KEY=... AWS_S3_BUCKET_NAME=... 여기서 중간 체크로 s3 사진을 한번 업로드 해보면 성공한다. 계정 B의 서버에서 계정 B의 S3로 이미지 업로드는 문제 없다는 뜻이다. 여기서 생길 수 있는 문제가 가용성이다. 만약 계정 b에서 업로드 했을 때, 계정 a에서도 업로드를 했어서 id가 겹친다면? 이거는 문제가 생길 수 있다. 여기서 다루지는 않지만, 추후 생각해볼 문제이다. 4.2 계정 B: Ec2 IAM Role 생성 1) Ec2 IAM Role 생성 IAM 사용자와 IAM Role은 다른 개념이다. IAM 사용자는 s3 업로드를 앞으로 담당할 것이고, 계정 B의 역할은 이번 s3 이전에서만 임시로 사용하고 삭제할 것이다. 계정 B IAM 사용자 → 앞으로 Spring의 S3 업로드 담당 계정 B EC2 IAM Role → 이번 S3 이전에만 임시 사용 EC2에서 IAM 역할을 생성한다. IAM -> 액세스 관리 -> 역할 -> 역할 생성 2) 인라인 정책 이제 인라인 정책을 작성해준다. --delete 옵션을 사용하지 않고 애플리케이션에서도 객체를 삭제하지 않는다면 ManageDestinationObjects에서 "s3:DeleteObject"는 제외해도 된다. 나는 제외하도록 하겠다. { "Version": "2012-10-17", "Statement": [ { "Sid": "ListSourceBucket", "Effect": "Allow", "Action": [ "s3:ListBucket", "s3:GetBucketLocation" ], "Resource": "arn:aws:s3:::{❗️계정-A-버킷명}" }, { "Sid": "ReadSourceObjects", "Effect": "Allow", "Action": [ "s3:GetObject" ], "Resource": "arn:aws:s3:::{❗️계정-A-버킷명}/*" }, { "Sid": "ListDestinationBucket", "Effect": "Allow", "Action": [ "s3:ListBucket", "s3:GetBucketLocation" ], "Resource": "arn:aws:s3:::{❗️계정-B-버킷명}" }, { "Sid": "ManageDestinationObjects", "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject" ], "Resource": "arn:aws:s3:::{❗️계정-B-버킷명}/*" } ] } 3) 검토 마지막으로 이름을 지정하고 검토 후 역할을 생성한다. 4) Iam Role과 Ec2 연결 이제 이 역할을 ec2랑 연결해야한다. 인스턴스 -> 작업 -> 보안 -> IAM 역할 수정 4.3 계정 A: 접근 권한 수정 1) 계정 A에서 계정 B의 role 접근 허용 계정 A에서도 위의 역할이 접근하는 것을 허용해야 한다. 계정 A의 기존 버킷 정책에도 계정 B Role을 허용해야하기 떄문에 아래처럼 추가했다. { "Version": "2012-10-17", "Statement": [ { "Sid": "AllowAccountBRoleToList", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::{❗️계정-B-ID:role}/{❗️계정-B-role명}" }, "Action": [ "s3:ListBucket", "s3:GetBucketLocation" ], "Resource": "arn:aws:s3:::{❗️계정-A-버킷명}" }, { "Sid": "AllowAccountBRoleToRead", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::{❗️계정-B-ID:role}/{❗️계정-B-role명}" }, "Action": "s3:GetObject", "Resource": "arn:aws:s3:::{❗️계정-A-버킷명}/*" } ] } 4.4 S3 버킷 데이터 이전하기 이제 계정 A의 데이터 복사해서 계정 B로 전송할 것이다. 1) aws cli 설치 먼저, 계정 B ec2에 접속하고, aws cli를 설치해준다. $ sudo snap install aws-cli --classic 그리고 아래의 명령어를 쳤을 때, 방금 만든 ec2 Iam role이 제대로 떠야한다. 만약 로그인이 필요하다고 뜬다면, ec2와 역할이 제대로 연결되었는지 확인해라. $ aws sts get-caller-identity 2) 터미널 변수값 입력 먼저 터미널에 변수 값을 입력한다. 이 값은 ssh가 끝나면 사라지는 임시값이다. SOURCE_BUCKET="실제-계정-A-버킷명" DEST_BUCKET="실제-계정-B-버킷명" 값이 잘 들어갔는지 확인한다. echo "$SOURCE_BUCKET" echo "$DEST_BUCKET" 3) 버킷 접근 확인 계정 A 버킷에 접근이 가능한지 확인한다. aws s3 ls "s3://$SOURCE_BUCKET" [트러블 슈팅] 문제: 아래처럼 차단되었다는 메세지가 떴다. 해결: 계정 A에서 네번쨰 체크박스를 해제해주었다. 그리고 계정 B 버킷 접근도 확인한다. aws s3 ls "s3://$DEST_BUCKET" 4) Dry Run으로 복사 대상 확인 먼저 dry run을 통해서 서로 데이터를 주고 받을 수 있는 상태인지 확인한다. aws s3 sync \ "s3://$SOURCE_BUCKET" \ "s3://$DEST_BUCKET" \ --source-region ap-northeast-2 \ --region ap-northeast-2 \ --dryrun 5) 실제 복사 Dry Run 결과에 문제가 없다면 --dryrun만 제거한다. 이제 실제 복사가 될 것이다. (--delete는 추가하지 않는다. 계정 B 버킷에만 존재하는 파일이 삭제될 수 있기 때문이다.) aws s3 sync \ "s3://$SOURCE_BUCKET" \ "s3://$DEST_BUCKET" \ --source-region ap-northeast-2 \ --region ap-northeast-2 6) 객체 수와 전체 크기 비교 제대로 복사가 되었는지 확인하기 위해서 객체 수와 크기를 비교한다. 계정 A 원본 버킷 확인 aws s3 ls "s3://$SOURCE_BUCKET" \ --recursive \ --summarize \ --human-readable 계정 B 대상 버킷 확인 aws s3 ls "s3://$DEST_BUCKET" \ --recursive \ --summarize \ --human-readable 각 명령의 마지막에 다음
옵시디언에서 한글을 타이핑하다 보면 글자가 씹히는 현상이 생긴다. 영어는 멀쩡한데 한글만 그렇다. 처음엔 macOS 입력기 문제인 줄 알았다. 구름 입력기로 바꿔보기도 하고, 옵시디언 설정을 이것저것 건드려봤는데 소용없었다. 원인 옵시디언은 Electron 기반 앱이다. Electron은 Chromium 엔진 위에서 돌아가는데, 이 Chromium의 IME(입력기) 처리와 macOS 한글 조합 방식이 충돌하면서 생기는 문제다. 옵시디언만의 버그가 아니라 Electron 앱 전반에서 나타나는 현상이다. Notion, VS Code, Slack 같은 앱에서도 같은 증 해결 방법 캐시를 삭제하면 된다. 100% 해결된다. 1. 옵시디언을 완전히 종료한다. 2. 터미널에서 캐시를 삭제한다. rm -rf ~/Library/Application\ Support/obsidian/Cache/Cache_Data/ 3. 옵시디언을 다시 실행한다. 이게 끝이다. 노트 데이터나 설정은 캐시 폴더에 없으니 삭제해도 아무것도 날아가지 않는다. 다른 Electron 앱도 같은 증상이면 Electron 앱들은 공통적으로 ~/Library/Application Support/ 아래에 캐시 폴더를 만든다. 해당 앱의 폴더에서 Cache 관련 디렉토리를 찾아 삭제하면 된다. # 예시: Notion rm -rf ~/Library/Application\ Support/Notion/Cache/Cache_Data/ # 예시: Slack rm -rf ~/Library/Application\ Support/Slack/Cache/Cache_Data/ 삭제 후 앱을 재시작하면 된다. 한글 입력 문제로 검색하면 입력기를 바꾸라거나 옵시디언 플러그인을 비활성화하라는 글이 많은데, 캐시 삭제가 가장 확실한 방법이었다. 원문: Sanghun Kang의 블로그
If you have ever searched for 100 grams to cups, you may have noticed something confusing: the answer changes depending on the ingredient. That is not a mistake. The reason is simple: Grams measure weight, while cups measure volume. Different ingredients have different densities, so 100 grams of flour will not fill the same amount of a cup as 100 grams of sugar, butter, oats, or honey. 100 Grams of Flour to Cups All-purpose flour is relatively light. Using a common reference of about 125 grams per US cup: 100g flour ≈ 0.8 cup This is why a recipe that uses 100 grams of flour cannot simply be treated as 1 cup. 100 Grams of Sugar to Cups Granulated sugar is heavier than flour. Around 200 grams of granulated sugar fits into one US cup. So: 100g sugar ≈ 0.5 cup The weight is still 100 grams, but the volume is very different. 100 Grams of Butter to Cups Butter is denser again. A US cup of butter is roughly 227 grams. That makes: 100g butter ≈ 0.44 cup 100 Grams of Oats to Cups Rolled oats are much lighter by volume. A cup of rolled oats is approximately 90 grams. So: 100g rolled oats ≈ 1.11 cups This is a good example of why there is no single answer for every ingredient. Why Ingredient Type Matters A useful way to think about it is this: The number of grams tells you how heavy an ingredient is. The number of cups tells you how much space it takes up. Two ingredients can weigh exactly the same but occupy very different amounts of space. That is why a proper conversion should always consider the ingredient being measured. If you want to compare more ingredients at the same weight, this 100 grams to cups conversion guide shows common values for flour, sugar, butter, oats, rice, honey, and other kitchen ingredients. 100 grams to cups conversion guide Cup Size Can Also Affect the Result There is one more detail to keep in mind. Not every cup standard has exactly the same volume. For example: US customary cup: about 236.6 ml US legal cup: 240 ml Metric cup: 250 ml Imperial cup: about 284 ml So if a recipe comes from another country, the cup standard may slightly change the final conversion. Final Thought There is no universal answer to 100 grams in cups. The correct result depends mainly on: the ingredient its density the cup standard how the ingredient is measured Once you understand the difference between weight and volume, these conversions become much easier to use correctly. ### how many cups are in 100 grams
사내에서 LLM을 쓰기 시작하면 몇 달 안에 꼭 이런 질문이 나온다. "이번 달 LLM 비용, 어느 팀이 어떤 기능에 얼마 썼어요?" 공급자 청구서에는 모델별 합계만 있다. 팀도, 사용자도, 기능도 없다. 그래서 이 질문에 답하려면 공급자와 코드 사이에 있는 게이트웨이 가 직접 장부를 써야 한다. 이 글은 그 장부를 어떻게 설계하는지 정리한 것이다. 목표는 한 문장이다. "이번 달 팀 X는 모델 Z와 기능 W에 $Y를 썼고, 이 숫자는 공급자 청구서와 $V 이내로 일치한다." 1. 요청마다 비용을 계산하고, 그 시점의 가격을 같이 저장한다 미터링 이벤트에는 토큰을 종류별로 나눠 담는다. 항목 왜 따로 담나 input_tokens 기본 입력 단가 cached_input_tokens 캐시 입력은 보통 기본가보다 훨씬 싸다. 합쳐 버리면 그 할인만큼 장부가 틀어진다 output_tokens 기본 출력 단가 reasoning_tokens 추론 모델의 내부 토큰. 요율이 따로 있는 경우가 많다 비용은 요청 시점에 계산하고, 그때의 단가를 이벤트에 스냅샷으로 남긴다. cost = in × price.input + cached × price.cached_input + out × price.output + reasoning × price.reasoning 나중에 가격이 바뀌어도 과거 어느 날의 비용이든 그대로 재현된다. 월말 정산이 "논쟁"이 아니라 "계산"이 되는 이유가 이 스냅샷이다. 공급자 응답에 사용량 정보가 빠진 경우에는 토큰 수를 추정하되 estimated: true 로 표시한다. 추정치 비중 을 지표로 추적하면, 공급자가 사용량 보고 방식을 바꿨을 때 바로 알 수 있다. 2. 귀속 축은 네 개면 충분하다 모든 이벤트에 아래 네 필드를 붙이고, 일 단위 롤업을 이 축 조합으로 만든다. 모델별 : alias 와 실제 provider_model_id 둘 다. 별칭은 우리 관점, 모델 ID는 공급자 관점이다. 팀별 : team (필요하면 cost_center ) 사용자별 : principal , 그리고 에이전트가 누군가를 대신해 호출했다면 on_behalf_of 기능별 : 등록된 feature 이름. 등록 안 된 호출은 unregistered 로 남겨서 그것도 리포트에 보이게 한다 롤업 테이블은 작게 유지한다. 중간 규모 조직이라면 하루 수천 행 수준이라, 대시보드와 정산이 ETL 프로젝트가 아니라 GROUP BY 한 줄로 끝난다. -- 이번 달 팀별 비용 SELECT team, alias_class, SUM(cost) AS cost FROM rollup_daily WHERE day >= date_trunc('month', now()) GROUP BY 1, 2 ORDER BY 3 DESC; 3. 예산은 "단계"와 "동작"으로 설계한다 일일 토큰 쿼터와 월 예산은 다른 물건이다. 쿼터는 새벽 3시의 폭주를 막고, 예산은 한 달을 관리한다. 둘 다 둔다. 예산에는 임계값마다 정해진 동작을 붙인다. 숫자는 조직마다 다르지만 동작은 설계로 정해 둔다. 단계 기본 임계값 동작 소프트 80% 거부 없음. 팀 채널에 소진 예측 알림 ("이 속도면 27일에 소진") 경고 95% 팀 리드에게도 알림. 응답 헤더에 X-LLM-Budget: 96% 같은 경고 포함 하드 100% 비싼 모델 클래스만 429 budget_denied 로 거부. 싼 클래스는 계속 허용 초과 110% 진행 중이던 요청 때문에 정산 시점에 넘긴 경우. 운영·재무에 알림 핵심은 하드 단계를 전체 차단이 아니라 클래스 선택적 으로 만드는 것이다. 100%에서 전부 막으면, 이미 쓰기로 한 저렴한 코딩 보조 기능까지 멈춰서 오히려 손해다. 무엇을 먼저 끊을지는 장애 순간이 아니라 미리 레지스트리 설정에 정책으로 적어 둔다. 파일럿 예산에는 반드시 만료일 을 둔다. 만료 없는 파일럿 예산은 사연이 붙은 상시 예산일 뿐이다. 4. 알림은 심각도 세 단계, 의미를 고정한다 비용 알림이 실패하는 전형적인 모습은 두 가지다. 알림이 너무 많아서 무시되거나, 중요한 알림이 엉뚱한 채널에 묻히거나. 심각도 의미 보내는 곳 info 예측 정보, 조치 불필요 팀 채널 warn 이번 주 안에 조치 필요 팀 채널 + 팀 리드 crit 지금 당장 위험 플랫폼 온콜 + 장애 관리 시스템 웹훅 예를 들어 "기능의 7일 비용이 30일 평균의 3배"는 warn, "팀 일일 비용이 평소의 5배"나 "단일 요청이 기준 금액 초과"는 crit이다. 같은 이벤트를 채널(사람용)과 웹훅(시스템용)으로 함께 보내고, 웹훅 페이로드 스키마는 고정해 둔다. 5. 대시보드에 넣지 않을 것도 정한다 대시보드는 네 개면 된다. 조직 전체 실시간 비용, 팀별 예산 소진, 모델별 비용(폴백 비율과 캐시 절감 포함), 사용자별 비용(공개 범위 제한). 요청별 비용은 대시보드에 넣지 않는다. 그건 트레이스에서 본다. 대시보드는 집계, 트레이스는 현미경이다. 현미경을 대시보드에 올리면 아무도 믿지 않는 로그 뷰어가 된다. 사용자별 비용 순위도 기본값은 팀 범위 공개로 둔다. 미터링 데이터 자체는 개인정보가 아니어도, "누가 가장 많이 쓰나" 순위표는 사회적 파급력이 있다. 6. 월말에는 공급자 청구서와 대사한다 게이트웨이 장부의 합계를 공급자 청구서와 비교하고, 허용 오차를 넘으면 crit 알림을 띄운다. 차이가 나는 흔한 원인은 캐시 토큰 미분리, 추정치 비중 증가, 가격표 갱신 누락이다. 요청 시점 가격 스냅샷이 있으면 이 비교가 쿼리 몇 줄로 끝난다. 정리 토큰은 종류별로 나누고, 요청 시점 가격을 이벤트에 스냅샷으로 남긴다 귀속 축은 모델·팀·사용자·기능 네 개 예산은 단계별 동작으로 설계하고, 하드 차단은 비싼 클래스에만 알림은 info/warn/crit 의미를 고정하고 채널과 웹훅을 분리 월말 대사로 장부를 검증한다 이 글은 제가 쓴 『AI 게이트웨이 플레이북』 한국어판 4장(쿼터, 예산, 비용 통제)을 요약한 것입니다. 책에는 모델 레지스트리, 키리스 인증과 6단계 RBAC, MCP 서버, RAG 어시스턴트, 운영 런북과 40개 항목 체크리스트까지 담았습니다. 책은 저의 실무 경험을 바탕으로 AI 도구의 도움을 받아 집필·번역했습니다. 한국어판 PDF: https://ko-fi.com/s/47455c0a7e
나는 소프트웨어 마에스트로 17기에서 그린고래라는 제품으로 활동하고 있다. 간단히 말해서, IoT 단말들을 활용해 실내 온도를 스케쥴링 하는 솔루션이다. 우리는 지난 9월 29일에 경기도 안양의 한 죽집에 방문해 첫 PoC 매장 설치를 완료했다. (매장에서 직접 설치를 하고 있는 은수씨, 상단 에어컨에 보면 그린고래 IR 단말(리모컨 역할)이 붙어있다.) 고난 끝에 드디어 제품이 사용자를 찾아갔다. 일전의 실패 사실 우리는 이미 7월 초에 제품을 완성하였다. 여름이 가기 전에 성과를 내야만 했기 때문이었다. 처음 해보는 하드웨어가 들어가는 제품임에도, 최대한 빠르게, 정말 말도 안되게 빨랐다 생각한다. 고작 한 달 만에 소프트웨어는 물론 적당히 돌아가는 하드웨어를 만들었다고... 판단했다. 아뿔싸!우린 빠르기만 더럽게 빨랐다! 제대로 들어맞지 않는 제어 알고리즘 연결되지 않는 블루투스 4일을 넘기지 못하는 배터리 부실한 접착제로 인한 기기 분리 에어컨에 닿지 않는 IR 신호 등등.. 실제로는 정말 다양한 문제가 터져나가며 4일만에 철수했었다.. 다행인 것은, 사장님이 제품이 완성되는대로 다시 설치해달라고 해주셨다는 것이다. 다행이다 정말 제품 개선 우리는 정신을 차리고 남은 여름이 가기 전에 제품을 고쳐 2차 PoC를 준비하기로 했다. 우선 AI로 급하게 기워낸 코드들을 바로 잡을 규약을 만들고, 처음부터 끝까지 완전한 핵사고날로 리팩토링 했다. 우리 백엔드 서버는 혼자서 IoT 기기 연결, 관리자 대시보드, 사용자 모바일 엔드포인트를 전부 맡기 때문에, 기술과 비즈니스 로직의 분리가 정말 중요하다고 판단했다. 이 작업을 거친 덕분에 코드는 드디어 '읽을 수 있는' 것이 되었고, AI 가 코드를 제 자리에 쓰고 오류를 내는 비율을 비약적으로 줄였다. 두번째는 제품 피보팅이었다. 우리는 기존에 완전 자율로 매장의 온도를 관리해주는 서비스를 기획하고 만들었으나, 데이터가 부족한 지금은 다소 무리라는 것을 인정하고 유저가 지정한 스케쥴을 바탕으로 에어컨을 운전하는 것으로 바꾸었다. 유저가 신경 써야 할 부분이 꽤 늘고 전기료 절감의 효과도 빛을 바래겠지만, 유저가 스케쥴과 매장 온도 리포트를 통해 데이터 기반 사고를 하도록 돕는 효과가 있고, 결과적으론 전기료 절감까지도 바랄 수 있다는 것이 우리 생각이다. 세번째는 하드웨어 개선이었다. 기존에는 내가 하드웨어까지 기술의 모든 것들을 아울렀지만, 스스로의 한계를 인정하고 팀원에게 하드웨어와 펌웨어 개발을 넘겼다. 팀원은 펌웨어를 깎는 것과 소자들을 바꾸는 것으로 기존 4일 정도이던 배터리 지속 시간을 3주 가량으로 늘렸다. 만약 PCBA가 된다면 거의 1년으로 비약적으로 늘 것으로 예상된다. 우리는 2달 간 이런 과정을 거쳤다. 아쉬운 점 다소 아쉬운 점은 고객과의 소통이 미흡했다는 점이다. 우리는 제품에 너무 몰입한 나머지, 고객이 기다리고 있다는 사실도 잊고, 고객이 먼저 연락하도록 했다. 사업을 하겠다는 사람이 이래선 되겠는가. 그래서 이번 스프린트에서는 그린고래 블로그와 패치노트, 뉴스레터를 만들기로 했다. 우리 제품의 발전이 궁금한 사람들은 뉴스레터를 통해 소식을 전달 받고, 쌓인 블로그와 패치노트는 그린고래를 처음 접하는 사람들에게 신뢰를 줄 것이라 생각된다. 앞으로? 이번 설치는 아주 깔끔했다. 접착제도 아주 튼튼하게 붙어주었고, 센서 데이터도 깔끔하게 들어온다. 자체 알고리즘으로 불안하게 운전되던 에어컨은 이제 사장님이 직접 지정한 스케쥴로 예상 가능한 움직임을 보여준다. 이제 한숨 돌리나 싶긴 한데, 그래도 아직 할 것들이 많이 남았다. 인프라 재정비 우선은 ... 인프라를 한번 재정비 해야한다. 우리가 아직 개발 서버가 없다.......... 운영 서버에서 테스트를 진행 할 수는 없으니, 개발 서버를 띄워야 한다. 또, 소마가 11월에 끝나는데 그때 서버를 아예 옮겨야하니, 이번 기회에 IaC를 공부해 적용해보기로 했다. 조만간 테라폼 도입기를 올려보고자 한다. IAC 01 손으로 만든 서버는 무엇을 잃나 — 명령형 스크립트와 선언형 코드 고객과의 소통 또 블로그와 패치노트도 만들어 보려한다. 기능 개발 사장님의 요청으로 몇개 기능을 더 개발해야하는데, zone 하나에 여러 Sensor 들이 붙을 수 있도록 해야하고, 스케쥴이 작동 할 때마다 사장님에게 알림이 가도록 해야한다. (이 기회에 SQS와 SNS를 한번 써보고자 한다.) AWS 07-1 큐와 알림 — SQS·SNS·SES·Amazon MQ 마지막으로 주간 온도 리포트를 보내드려야 한다. 이 기능은 아직 기획 중이다. 설치 직후 찍었다. 자랑스런 내 팀이다. 성과를 하나 올렸지만, 이제야 첫 걸음을 딛었을 뿐이다. 지금까지와 같이 성실하게 임해야겠다.