디자인 패턴이란? 디자인 패턴이란 소프트웨어 디자인 과정에서 자주 발생하는 문제들에 대한 전형적인 해결책이다. 코드에서 반복되는 디자인 문제들을 해결하기 위해 맞춤화할 수 있는 미리 만들어진 청사진이라고 할 수 있다. 패턴은 재사용할 수 있는 코드 조각이 아니라 특정 문제를 해결하는 방식을 알려주는 일반적인 개념이다. 패턴은 알고리즘과 자주 혼동된다. 두 개념 모두 알려진 문제에 대한 일반적인 해결책을 설명하기 때문이다. 알고리즘은 어떤 목표를 달성하기 위해 따라야 할 명확한 일련의 절차를 정의하지만, 패턴은 해결책에 대한 더 상위 수준의 설명이다. 패턴이 왜 필요한가? 디자인 패턴은 소프트웨어 디자인의 일반적인 문제들에 대해 시도되고 검증된 해결책들을 모은 것이다. 이러한 문제들을 다루지 않더라도 패턴을 알고 있으면 여전히 쓸모가 있으나, 그 이유는 패턴을 배우게 되면 객체 지향 디자인의 원칙들을 사용해 많은 종류의 문제를 해결하는 방법들을 배울 수 있기 때문이다. 디자인 패턴의 종류 디자인 패턴의 성경이라고도 불리는 GoF의 디자인 패턴에서 소개하는 23가지 패턴이 존재한다. 이러한 디자인 패턴은 크게 3 종류로 나뉜다. 생성 패턴 구조 패턴 행동 패턴 생성 패턴 팩토리 메서드 : 부모 클래스에서 객체를 생성하기 위한 인터페이스를 정의하고, 자식 클래스에서 어떤 클래스의 인스턴스를 생성할지 결정하는 패턴이다. 추상 팩토리 메서드 : 관련된 객체들의 집합을 생성하는 인터페이스를 제공하며, 구체적인 팩토리 클래스를 통해 객체 생성을 추상화하는 패턴이다. 빌더 패턴 : 복잡한 객체의 생성 과정을 단순화하고, 객체를 단계적으로 생성하며 구성하는 패턴이다. 프로토타입 패턴 : 객체를 복제하여 새로운 객체를 생성하는 패턴으로, 기존 객체를 템플릿으로 사용하는 패턴이다. 싱글턴 패턴 : 클래스에 인스턴스가 하나만 있도록 하면서 해당 인스턴스에 대한 전역 접근 지점을 제공하는 패턴이다. 구조 패턴 어댑터 패턴 : 인터페이스 호환성을 제공하지 않는 클래스를 사용하기 위해 래퍼를 제공하는 패턴이다. 브릿지 패턴 : 추상화와 구현을 분리하여 두 가지를 독립적으로 확장할 수 있는 패턴이다. 컴포지트 패턴 : 개별 객체와 복합 객체를 동일하게 다루어, 트리 구조의 객체를 구성하는 패턴이다. 데코레이터 패턴 : 객체들을 새로운 행동들을 포함한 특수 래퍼 객체들 내에 넣어서 위 행동들을 해당 객체들에 연길시키는 패턴이다. 퍼사드 패턴 : 서브시스템을 더 쉽게 사용할 수 있도록 단순한 인터페이스를 제공하는 패턴이다. 플라이웨이트 패턴 : 공유 가능한 객체를 통해 메모리 사용을 최적화하는 패턴이다. 프록시 패턴 : 다른 객체에 대한 대리자를 제공하여 접근 제어, 지연 로딩을 구현하는 패턴이다. 행위 패턴 옵저버 패턴 : 객체 간의 일대다 종속 관계를 정의하여 한 객체의 상태 변경이 다른 객체들에게 알려지도록 한다. 전략 패턴 : 알고리즘을 정의하고, 실행 중에 선택할 수 있게 한다. 커맨드 패턴 : 요청을 객체로 캡슐화하여 요청을 매개변수화 하고, 요청을 큐에 저장하거나 로깅하고 실행을 지연시킨다. 상태 패턴 : 객체의 상태를 캡슐화하고, 상태 전환을 해당 객체가 관리한다. 책임 연쇄 패턴 : 요청을 보내는 객체와 이를 처리하는 객체를 분리하여, 다양한 처리자 중 하나가 요청을 처리한다. 방문자 패턴 : 객체 구조를 순회하면서 다양한 연산을 수행할 수 있게 한다. 인터프리터 패턴 : 언어나 문법에 대한 해석기를 제공하여, 주어진 언어로 표현된 문제를 해결하는 패턴이다. 메멘토 패턴 : 객체의 내부 상태를 저장하고 복원할 수 있는 기능을 제공하는 패턴이다. 중재자 패턴 : 객체 간의 상호 작용을 캡슐화하여, 객체 간의 직접적인 통신을 방지하는 패턴이다. 템플릿 메서드 패턴 : 부모 클래스에서 알고리즘의 구조를 정의하면서 자식 클래스에 각 단계의 구현을 제공하는 패턴이다. 이터레이터 패턴 : 컬렉션 내의 요소들에 접근하는 방법을 표준화하여 컬렉션 내부 구조에 독립적으로 접근할 수 있는 패턴이다.
Pour corriger une fausse information sur ChatGPT, il faut d’abord déterminer où se trouve l’erreur. Une réponse incorrecte dans votre conversation peut être rectifiée en fournissant une preuve. Une information mémorisée sur vous se corrige dans la mémoire de votre compte. Une affirmation erronée sur votre entreprise demande souvent un travail plus large : examiner les sources consultées, mettre à jour les pages publiques et signaler la réponse. Ces actions ont des effets différents. Dire à ChatGPT qu’il se trompe peut améliorer votre échange, mais ne change pas automatiquement ce que les autres utilisateurs verront. Pourquoi ChatGPT peut-il donner une information fausse ? ChatGPT peut produire une phrase précise et convaincante alors que le fait avancé est incorrect. Le problème peut venir d’une question ambiguë, d’une confusion entre deux personnes ou entreprises, d’une information ancienne, d’une page web erronée ou d’une conclusion que l’assistant tire sans preuve suffisante. Il faut donc éviter une première erreur de diagnostic : supposer que toute fausse réponse provient de votre site. ChatGPT peut s’appuyer sur des sources externes. Il peut aussi mal interpréter une source correcte ou répondre sans disposer d’éléments fiables pour vérifier le détail demandé. Prenons une entreprise qui a déménagé de Lyon à Lille. Si ChatGPT mentionne encore Lyon, plusieurs explications sont possibles. L’ancienne adresse figure peut-être sur sa page de contact, dans un annuaire professionnel ou dans un article jamais actualisé. Mais l’assistant peut également confondre une ancienne implantation avec son siège actuel. La correction dépendra de ce que vous découvrirez. Commencez par conserver une trace de l’erreur : Copiez la question exacte que vous avez posée. Enregistrez la réponse complète et sa date. Notez les sources affichées, s’il y en a. Écrivez la version correcte du fait. Réunissez un document ou une page qui permet de la vérifier. Cette petite fiche vous évitera de travailler sur une impression générale du type « ChatGPT raconte n’importe quoi sur nous ». Elle vous donnera un fait à corriger, une preuve et un moyen de contrôler le résultat. Comment corriger une réponse fausse dans votre conversation ? La manière la plus rapide consiste à citer la phrase incorrecte, puis à fournir l’information juste avec son contexte. Demandez ensuite une réponse révisée et invitez ChatGPT à reconnaître les points qu’il ne peut pas vérifier. Vous pouvez utiliser une formulation comme celle-ci : « Vous indiquez que notre entreprise est basée à Lyon. Cette information n’est plus exacte. Notre siège se trouve à Lille depuis septembre 2026. Reprenez votre réponse avec cette correction et précisez si d’autres éléments restent incertains. » Adaptez naturellement l’exemple à votre situation. Pour un prix, donnez la date à laquelle il s’applique. Pour une fonctionnalité, précisez le produit ou la formule concernés. Pour une personne, indiquez la fonction exacte et évitez de divulguer des informations privées qui ne sont pas nécessaires à la correction. Si ChatGPT a cité une source, ouvrez-la et vérifiez ce qu’elle dit réellement. Il arrive qu’une page soit correcte, mais que l’assistant en tire une conclusion trop large. Dans ce cas, expliquez la nuance : « Cette page décrit notre ancien bureau, pas notre siège actuel. » La réponse obtenue après votre correction peut être utile pour votre travail immédiat. Elle ne prouve cependant pas que tous les utilisateurs recevront désormais la même information. Pour une erreur publique concernant votre marque, il faut poursuivre l’enquête. Comment signaler une information incorrecte à OpenAI ? Utilisez l’option de retour négatif associée au message concerné et suivez les indications affichées. Lorsque vous pouvez ajouter des détails, décrivez l’erreur de façon vérifiable : la phrase fautive, la version correcte et la preuve correspondante. C’est la procédure indiquée dans l’aide d’OpenAI pour signaler une réponse dans ChatGPT. Un retour utile ressemble à ceci : « La réponse affirme que notre société vend uniquement aux professionnels. C’est incorrect. Notre boutique accepte également les commandes de particuliers, comme l’indiquent nos conditions de vente actuelles. » Évitez les signalements qui mélangent plusieurs problèmes. Si la réponse se trompe à la fois sur l’adresse, le dirigeant et les services de votre entreprise, identifiez chaque affirmation séparément. L’examen sera plus simple, et vous pourrez vérifier ensuite quels points réapparaissent. Le signalement ne fonctionne pas comme une demande de modification immédiate d’une fiche d’entreprise. Vous n’obtenez pas le contrôle éditorial des futures réponses. Si une page publique contient l’information erronée, sa correction reste une étape distincte et souvent prioritaire. Comment savoir d’où vient une fausse information sur votre entreprise ? Reposez la question de plusieurs façons et regardez si ChatGPT fournit des sources. Cherchez ensuite l’affirmation incorrecte sur votre site et sur les pages publiques consacrées à votre entreprise. Une seule question ne suffit pas toujours. Si vous cherchez à vérifier vos horaires, comparez par exemple : « Quels sont les horaires de cette entreprise ? » « Cette entreprise est-elle ouverte le samedi ? » « Puis-je contacter son équipe aujourd’hui ? » Ces questions peuvent conduire à des réponses différentes parce qu’elles n’exigent pas exactement le même fait. Notez celles qui produisent l’erreur. Une réponse fautive répétée avec la même source vous donne une piste plus solide qu’un résultat isolé. Vérifiez ensuite les endroits où l’information apparaît. Commencez par la page officielle la plus pertinente : contact, tarifs, équipe, produit ou conditions de vente. Poursuivez avec vos profils publics, les annuaires, les articles de presse et les comparatifs qui vous mentionnent. Soyez attentif aux contradictions internes. Votre page de contact peut afficher Lille tandis qu’une ancienne page « À propos » indique encore Lyon. Une FAQ peut annoncer une fonctionnalité disponible alors que la page produit précise qu’elle ne l’est plus. Même si chaque phrase a eu un sens à une certaine date, leur coexistence rend votre situation actuelle difficile à comprendre. Lorsque ChatGPT affiche des citations, elles constituent des indices, pas une preuve absolue de la cause. Selon la documentation d’OpenAI sur ChatGPT Search, les résultats et les citations peuvent eux aussi être incomplets ou inexacts. Lisez donc la page citée avant de conclure qu’elle est responsable de la réponse. Ancienne adresse Où chercher d’abord : page de contact et profils publics. Action la plus directe : actualiser les coordonnées. Mauvais prix Où chercher d’abord : page tarifaire et comparatifs. Action la plus directe : clarifier le prix et sa date. Fonctionnalité inexistante Où chercher d’abord : page produit et documentation. Action la plus directe : corriger la description. Mauvais dirigeant Où chercher d’abord : présentation officielle et articles tiers. Action la plus directe : mettre à jour les informations. Confusion entre deux marques Où chercher d’abord : noms, présentations et sources citées. Action la plus directe : rendre l’identité plus explicite. Comment corriger les informations publiées à la source ? Modifiez la page qui devrait faire autorité sur le fait concerné, puis vérifiez que le reste de votre présence publique raconte la même chose. Une correction efficace est claire, datée lorsque le changement compte, et facile à comprendre hors de son contexte. Pour une adresse, écrivez explicitement l’adresse actuelle sur votre page de contact. Si votre ancienne implantation crée une confusion persistante, vous pouvez préciser qu’elle n’est plus utilisée. Pour un prix, indiquez ce qui est inclus et évitez de laisser une ancienne grille accessible sans explication. Pour une fonctionnalité, distinguez ce qui est disponible aujourd’hui de ce qui est simplement envisagé. Ne comptez pas sur une formule vague. « Notre offre a évolué » ne dit pas si l’ancien tarif est encore valable. « Nous accompagnons maintenant d’autres marchés » ne précise pas quels pays sont effectivement desservis. Une personne qui découvre votre entreprise doit pouvoir retrouver la réponse correcte sans interprétation. Pensez aussi aux pages que vous contrôlez indirectement : profils d’établissement, pages de réseaux sociaux, descriptions sur des plateformes professionnelles ou fiches de partenaires. Pour une page publiée par un tiers, contactez son éditeur avec une demande courte. Indiquez la phrase à rectifier, la formulation proposée et la preuve. Vous obtiendrez plus facilement une correction concrète qu’en demandant de manière générale une « mise à jour de notre présence en ligne ». Conservez une liste des pages corrigées et de celles dont vous attendez une réponse. Si la fausse information continue d’apparaître dans ChatGPT, cette liste vous aidera à repérer les sources encore actives. Comment corriger une information enregistrée dans la mémoire de ChatGPT ? Si ChatGPT répète une information fausse uniquement dans vos échanges, vérifiez la mémoire de votre compte. Dites-lui que le fait mémorisé est incorrect ou dépassé, puis contrôlez les informations enregistrées dans les réglages. L’aide d’OpenAI explique qu’un souvenir peut être corrigé ou supprimé. Par exemple, ChatGPT peut continuer à vous présenter comme salarié d’une entreprise que vous avez quittée. Une correction explicite serait : « Je ne travaille plus pour cette entreprise. Supprime cette information de ta mémoire. » Vérifiez ensuite que l’ancien fait n’apparaît plus parmi les souvenirs enregistrés. Il faut distinguer la mémoire explicite de l’influence possible d’anciennes conversations. Si vous avez souvent parlé d’un ancien poste ou d’un ancien projet, regardez également le contexte de vos échanges et les réglages liés à l’historique. Une réponse adaptée à votre compte ne signifie pas que ChatGP
#client.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> // 소켓 생성 및 통신 관련 함수 (socket, connect, send, recv 등) #include <arpa/inet.h> // IP 주소 변환 및 바이트 순서 변환 함수 (htons, inet_pton 등) #define PORT 8080 #define BUF_SIZE 1024 int main(void) { // 1. TCP 소켓 생성 (IPv4, TCP 통신) int sock_fd = socket(AF_INET, SOCK_STREAM, 0); if (sock_fd == -1) { perror("socket"); exit(1); } // 2. 서버 주소 정보 구조체 설정 (IPv4, Port 8080) struct sockaddr_in server_addr = { 0 }; // 쓰레기 값 방지를 위한 초기화 server_addr.sin_family = AF_INET; // IPv4 주소 체계 server_addr.sin_port = htons(PORT); // Port 번호를 네트워크 바이트 순서(Big-Endian)로 변환 // 문자열 IP 주소("127.0.0.1")를 바이너리 네트워크 주소로 변환하여 server_addr.sin_addr에 저장 if (inet_pton( AF_INET, "127.0.0.1", &server_addr.sin_addr ) != 1){ perror("inet_pton"); close(sock_fd); exit(1); } // 3. 서버에 TCP 연결 요청 (3-way handshake 수행) if (connect( sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr) ) == -1) { perror("connect"); close(sock_fd); exit(1); } printf("connected to server\n"); const char *message = "hello server"; // 4. 서버로 메시지 전송 if (send( sock_fd, message, strlen(message), 0 ) == -1) { perror("send"); close(sock_fd); exit(1); } char buffer[BUF_SIZE] = {0}; // 5. 서버로부터 응답 수신 (데이터가 올 때까지 대기/Blocking) ssize_t n = recv( sock_fd, buffer, sizeof(buffer) - 1, // 문자열 끝 null 문자를 남겨두기 위해 -1 0 ); if (n == -1) { perror("recv"); close(sock_fd); exit(1); } // 수신한 데이터 끝에 null 문자('\0')를 붙여 안전한 문자열로 완결 buffer[n] = '\0'; printf("server response: %s\n", buffer); // 6. 소켓 자원 해제 및 연결 종료 close(sock_fd); return 0; } #server.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #define PORT 8080 #define BUF_SIZE 1024 int main(void) { int server_fd = socket(AF_INET, SOCK_STREAM, 0); if (server_fd == -1) { perror("socket"); exit(1); } //열려있다면, 무시하고 다시 덮어씀 int opt = 1; if (setsockopt( server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt) ) == -1) { perror("setsockopt"); close(server_fd); exit(1); } //IPv4 주소 구조체 생성, 설정 struct sockaddr_in addr = { 0 }; addr.sin_family = AF_INET; addr.sin_port = htons(PORT); addr.sin_addr.s_addr = INADDR_ANY; //설정한 주소 정보를 서버 소켓에 바인딩 if (bind(server_fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { perror("bind"); close(server_fd); exit(1); } //소켓을 연결 수신 대기 상태로 전환 if (listen(server_fd, 5) == -1) { perror("listen"); close(server_fd); exit(1); } printf("listening of port %d...\n", PORT); // 여기서 block int client_fd = accept(server_fd, NULL,NULL); if (client_fd == -1) { perror("accept"); close(server_fd); exit(1); } printf("client connected\n"); printf("server_fd = %d\n", server_fd); printf("client_fd = %d\n", client_fd); char buffer[BUF_SIZE] = { 0 }; ssize_t n = recv( client_fd, buffer, sizeof(buffer) - 1, 0 ); if (n == -1) { perror("recv"); close(client_fd); close(server_fd); exit(1); } buffer[n] = '\0'; printf("recieved: %s\n", buffer); const char *response = "hello client"; if (send( client_fd, response, strlen(response), 0 ) == -1) { perror("send"); } close(client_fd); close(server_fd); return 0; } 이번 server.c / client.c (B5 TCP Socket 기초) 실습에서 질문하셨던 핵심 개념과 답변들을 한눈에 복습 및 기록하실 수 있도록 요약·정리해 두었습니다. 1. sockaddr_in 구조체 멤버명 sin_family 의 의미 sin = s ocket in ternet (인터넷 소켓) family = 주소 체계 / 계열 의미: struct sockaddr_in 구조체 내부 멤버의 접두어( sin_ )이며, sin_family = AF_INET 은 IPv4 인터넷 주소 체계를 사용하겠다는 의미입니다. 2. &server_addr.sin_addr 의 역할 개념: server_addr 구조체 내부의 IP 주소 필드( sin_addr )에 대한 메모리 주소(Pointer)입니다. 역할: inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr) 와 같이, 문자열 IP를 네트워크 바이너리로 변환한 결과를 어느 메모리 위치에 저장할지 넘겨주는 목적 으로 사용됩니다. 3. TCP Three-way Handshake의 주체 및 시점 주체: C 언어 코드( accept 등)가 아니라 OS 커널의 TCP/IP 스택 이 백그라운드에서 실행합니다. 시점: 서버가 listen() 을 호출해 LISTEN 상태가 된 후, 클라이언트가 connect() 를 호출할 때 일어납니다. accept() 의 실제 역할: Handshake를 직접 하는 것이 아니라, 커널 내부 Accept Queue에 이미 Handshake가 완료되어 들어온 연결을 꺼내와서 애플리케이션용 File Descriptor( client_fd )를 반환 합니다. 4. recv() / accept() 에서의 스레드 블로킹 (Blocking) 스레드 상태: 연결 요청이나 데이터가 올 때까지 스레드가 OS 커널에 의해 Waiting (Blocked) 상태로 전환 됩니다. 특징: 이 대기 상태 동안 해당 스레드는 CPU 자원을 전혀 소모하지 않으며 , 커널에 이벤트(패킷 수신 등)가 발생하면 깨어나 동작을 재개합니다. 5. TCP MSS와 Sequence Number 관리 개념: TCP 통신에서 MSS(최대 세그먼트 크기) 협상과 Sequence/ACK Number 를 통한 데이터 순서 보장 및 손실 재전송 제어는 TCP의 핵심 동작 방식입니다. 담당 영역: 이 모든 과정 역시 개발자가 직접 코딩하는 것이 아니라 OS 커널이 자동으로 관리 하며, C 소켓 API( send , recv )는 그 결과를 애플리케이션으로 가져오는 인터페이스 역할을 합니다. 6. TCP의 Byte Stream 특성과 가상 예시 개념: TCP는 메시지 경계(Boundary)가 없는 '연속된 바이트의 흐름(Byte Stream)'입니다. send() 2번 예시의 의미: 작성하신 클라이언트 코드는 send() 를 1번만 부르고 있으나, "만약 클라이언트가 send() 를 2번 호출한다면?"이라는 가상의 예시입니다. 결과: 클라이언트가 send() 를 2번 나누어 부르더라도 네트워크 상황이나 OS 버퍼 상태에 따라 서버는 recv() 1번으로 통째로 받거나 여러 번 쪼개서 받을 수 있습니다. 따라서 추후 Protocol Framing(구분자 추가, 길이 명시 등) 처리 로직이 필요합니다. 7. recv() 호출 시 sizeof(buffer) - 1 을 사용하는 이유 목적: 수신된 데이터 맨 끝에 C언어 문자열 종료 문자인 NULL( '\0' )을 추가할 1바이트 공간을 확보 하기 위함입니다. 이유: recv() 는 데이터 뒤에 NULL 문자를 자동으로 붙여주지 않습니다. 만약 버퍼 크기 전체(1024)를 수신받으면 buffer[n] = '\0' 시 메모리 침범(Buffer Overflow)이 발생하므로, 최대 1023바이트만 받도록 제한하여 안전하게 문자열로 완결 짓습니다. 8. recv() 함수의 추상화 수준 개념: recv() 는 하드웨어(NIC) 인터럽트, IP/TCP 헤더 검증, Checksum, 패킷 재정렬, 손실 재전송, 커널 ➔ 유저 메모리 복사 등 복잡한 네트워크 스택의 처리를 C 함수 호출 한 줄로 은닉한 고급 추상화 API 입니다.
Kafka 전용 코드 리뷰: 주문 event가 수집되기까지 1. 메시지의 길 POST /api/orders → OrderService → MySQL 주문 + outbox (같은 DB 트랜잭션) OutboxScheduler → OutboxPublisher → KafkaOrderEventSender → orders.paid 토픽 orders.paid 토픽 → AnalyticsConsumer → AnalyticsService → MySQL 수집 기록 토픽 은 Kafka의 메시지 이름표가 붙은 저장 공간이다. 파티션 은 한 토픽을 나눈 줄이다. 프로듀서 는 메시지를 보내고 컨슈머 는 읽는다. 이 코드에서는 OrderEvent 한 개가 주문 완료 사건 한 개를 나타낸다. 2. 주문과 Kafka를 한 번에 직접 처리하지 않는 이유 MySQL과 Kafka는 서로 다른 시스템이다. 주문을 DB에 저장한 직후 서버가 꺼지면 Kafka 전송이 빠질 수 있다. 그래서 OrderService 가 주문과 outbox 행을 같은 MySQL 트랜잭션 에 저장한다. 예약 작업이 나중에 outbox를 읽어 Kafka로 보낸다. Kafka 확인을 받은 뒤에만 대기 행을 지운다. 서버가 멈춘 시점 남는 것 다시 시작하면 DB 트랜잭션이 끝나기 전 주문과 outbox가 함께 취소됨 새 요청으로 다시 시도 DB 커밋 후 Kafka 전송 전 주문과 outbox가 남음 예약 작업이 전송 Kafka 확인 후 outbox 삭제 전 Kafka 메시지와 outbox가 모두 남을 수 있음 다시 전송될 수 있어 수신자가 event_id 로 중복 저장을 막음 이 방식은 최소 한 번 전달 이다. 결제 요청을 한 번만 처리하는 Idempotency-Key , Kafka 프로듀서 설정의 enable.idempotence , 수집 표의 event_id 중복 방지는 각각 다른 구간의 중복을 다룬다. 3. 파티션 3개와 consumer 3개를 읽는 법 OrderTopicConfig 는 orders.paid 토픽에 파티션 3개를 요청한다. AnalyticsConsumer 의 concurrency = "3" 은 애플리케이션 인스턴스 한 대에서 최대 세 소비자를 만든다는 뜻이다. 여러 인스턴스가 모두 coffee-analytics 그룹을 사용하면 파티션 3개를 나눠 읽는다. 이 그룹에서 동시에 유효한 파티션 담당자는 최대 3개다. 이 로컬 구성은 Kafka 브로커가 한 대이고 복제본도 한 개라 브로커 장애에 대비한 구성은 아니다. 4. 코드와 줄 바로 위 설명 1. build.gradle // 플러그인은 Gradle에 기능을 더합니다. Java 컴파일과 Spring Boot 실행 기능을 여기서 켭니다. plugins { id 'java' // Spring Boot는 웹 서버를 쉽게 만드는 Java 도구입니다. 버전을 적어 같은 환경으로 빌드합니다. id 'org.springframework.boot' version '3.5.7' id 'io.spring.dependency-management' version '1.1.7' } group = 'com.example' version = '0.0.1-SNAPSHOT' java { // 이 프로젝트가 Java 17 문법을 사용한다는 뜻입니다. sourceCompatibility = JavaVersion.VERSION_17 } repositories { // 필요한 라이브러리를 내려받을 공개 저장소입니다. mavenCentral() } dependencies { // HTTP 요청을 받고 JSON 응답을 보내는 기능입니다. implementation 'org.springframework.boot:spring-boot-starter-web' // Java 코드에서 SQL을 실행하는 기능입니다. 잔액을 바꾸는 SQL을 직접 쓰기 위해 선택했습니다. implementation 'org.springframework.boot:spring-boot-starter-jdbc' // 사용자 ID와 금액이 올바른지 @Min, @Max 등으로 검사하는 기능입니다. implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-cache' // Redis에 연결합니다. Redis는 빠른 조회용 저장소이고 결제의 최종 기록은 MySQL에 둡니다. implementation 'org.springframework.boot:spring-boot-starter-data-redis' // Kafka로 주문 완료 메시지를 보내고 받는 기능입니다. implementation 'org.springframework.kafka:spring-kafka' // Flyway는 SQL 파일을 번호 순서대로 실행해 DB 테이블을 만듭니다. implementation 'org.flywaydb:flyway-core' implementation 'org.flywaydb:flyway-mysql' // 실행할 때 MySQL에 접속하는 드라이버가 필요합니다. runtimeOnly 'com.mysql:mysql-connector-j' compileOnly 'org.projectlombok:lombok' // Lombok이 컴파일 중 builder() 코드를 자동 생성합니다. 코드에 직접 적지 않아도 호출할 수 있습니다. annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' // 자동 테스트에서는 설치가 간단한 메모리 DB인 H2를 사용합니다. testRuntimeOnly 'com.h2database:h2' } tasks.withType(JavaCompile).configureEach { // 더 최신 JDK로 빌드하더라도 Java 17에 없는 기능을 잘못 쓰지 않도록 막습니다. options.release = 17 } tasks.named('test') { // JUnit은 자동 테스트를 실행하는 도구입니다. 이 설정이 JUnit 5 테스트를 켭니다. useJUnitPlatform() } 2. compose.yaml # Docker Compose가 함께 실행할 프로그램들을 나열합니다. 여기서는 MySQL, Kafka, Redis입니다. services: mysql: # MySQL 8.4를 사용합니다. MySQL에는 잔액, 주문, 발행 대기 메시지를 저장합니다. image: mysql:8.4 environment: MYSQL_DATABASE: coffee MYSQL_USER: coffee # 로컬 연습용 비밀번호입니다. 실제 서비스에서는 코드에 비밀번호를 그대로 두면 안 됩니다. MYSQL_PASSWORD: coffee MYSQL_ROOT_PASSWORD: root ports: # 왼쪽 13306은 내 컴퓨터의 포트, 오른쪽 3306은 컨테이너 안의 포트입니다. - "13306:3306" volumes: # DB 파일을 Docker 볼륨에 보관합니다. 컨테이너를 다시 만들어도 실습 데이터가 남을 수 있습니다. - mysql_data:/var/lib/mysql healthcheck: test: ["CMD-SHELL", "mysqladmin ping -h localhost -u root -proot"] interval: 3s timeout: 3s retries: 10 kafka: # Kafka는 주문 완료 사건을 다른 프로그램에 전달합니다. 이 파일은 브로커 한 대만 실행합니다. image: apache/kafka:4.1.2 ports: - "9092:9092" redis: # Redis는 메뉴 캐시와 최근 주문 횟수 ZSET을 저장합니다. Redis 데이터가 없어져도 MySQL에서 다시 만들 수 있습니다. image: redis:7.4.11-alpine3.21 ports: - "6379:6379" healthcheck: # healthcheck는 Redis가 명령에 응답할 준비가 됐는지 확인합니다. test: ["CMD", "redis-cli", "ping"] interval: 3s timeout: 3s retries: 10 volumes: mysql_data: 3. src/main/resources/application.yml # Spring Boot의 실행 설정입니다. 들여쓰기로 어떤 설정이 어느 기능에 속하는지 구분합니다. spring: datasource: # ${DB_URL:기본값}은 환경변수 DB_URL이 있으면 그 값을 쓰고, 없으면 기본값을 쓴다는 뜻입니다. # 시간을 UTC로 맞춰 서버마다 주문 시각과 7일 계산이 달라지지 않게 합니다. url: ${DB_URL:jdbc:mysql://localhost:13306/coffee?connectionTimeZone=UTC&forceConnectionTimeZoneToSession=true} username: ${DB_USER:coffee} password: ${DB_PASSWORD:coffee} # 앱이 시작할 때 번호가 붙은 DB 변경 SQL을 실행합니다. flyway: enabled: true kafka: # Kafka가 실행 중인 주소입니다. bootstrap-servers: ${KAFKA_BOOTSTRAP_SERVERS:localhost:9092} consumer: key-deserializer: org.apache.kafka.common.serialization.StringDeserializer value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer # 이 소비자 그룹에 저장된 읽기 위치가 없을 때만 토픽 앞에서부터 읽습니다. # 처음 소비하는 그룹이라 읽은 위치 기록이 없으면 가장 오래된 메시지부터 읽습니다. auto-offset-reset: earliest properties: # JSON의 타입 정보를 Java 객체로 바꿀 때 허용하는 패키지를 좁힙니다. # 주문 이벤트 DTO가 옮겨진 새 패키지에서 Kafka JSON 객체를 읽습니다. # JSON 메시지를 Java 객체로 바꿀 때 허용할 코드 패키지를 제한합니다. spring.json.trusted.packages: com.example.coffee.order.dto producer: key-serializer: org.apache.kafka.common.serialization.StringSerializer value-serializer: org.springframework.kafka.support.serializer.JsonSerializer # 설정된 복제본들이 기록을 확인해야 전송 성공으로 간주합니다. 이 실습은 복제본이 1개입니다. # Kafka가 메시지를 받았다고 확인할 때까지 기다리도록 합니다. 브로커가 한 대이므로 복제 내구성은 별개입니다. acks: all properties: # 프로듀서의 재전송 때문에 같은 레코드가 중복 기록되는 일을 줄입니다. 결제 요청 멱등성과는 별개입니다. # Kafka 생산자 내부의 재시도 중복을 줄입니다. HTTP 결제 요청 중복 방지와는 다른 기능입니다. enable.idempotence: true data: redis: # Redis 주소도 환경변수로 바꿀 수 있습니다. host: ${REDIS_HOST:localhost} port: ${REDIS_PORT:6379} connect-timeout: 2s timeout: 2s cache: type: redis # 이전 메뉴 객체를 담은 Redis 캐시와 새 DTO 패키지의 캐시를 분리합니다. cache-names: menus-v2 redis: # 캐시 값을 5분 뒤 만료시킵니다. 만료되면 다음 조회에서 MySQL을 다시 읽습니다. time-to-live: 5m orders: # 주문 완료 사건이 지나가는 Kafka 토픽의 이름입니다. # 주문 완료 메시지를 보낼 Kafka 토픽 이름입니다. 토픽은 메시지를 담는 통로입니다. topic: orders.paid # outbox 발행과 Redis 집계를 실행할 예약 작업 스레드를 둘로 설정합니다. spring.task.scheduling.pool.size: 2 4. src/main/resources/db/migration/V1__init.sql -- 메뉴 표를 만듭니다. 한 행이 커피 메뉴 하나입니다. CREATE TABLE menus ( id BIGINT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100) NOT NULL UNIQUE, -- 가격을 정수 원 단위로 저장합니다. 소수점 반올림 오류를 피하기 쉽습니다. price BIGINT NOT NULL CHECK (price > 0) ); -- 사용자 한 명의 현재 포인트 잔액을 저장합니다. CREATE TABLE point_accounts ( user_id BIGINT PRIMARY KEY, -- CHECK는 잔액이 음수가 되거나 정한 최대값을 넘지 못하게 DB에서도 검사합니다. balance BIGINT NOT NULL DEFAULT 0 CHECK (balance >= 0 AND balance <= 1000000000000) ); -- 결제가 끝난 주문을 저장합니다. 메뉴 가격이 나중에 변해도 당시 결제액을 기억해야 합니다. CREATE TABLE orders ( id BIGINT AUTO_INCREMENT PRIMARY KEY, -- REFERENCES는 해당 사용자 계정이 실제로 존재해야 한다는 외래키 규칙입니다. user_id BIGINT NOT NULL REFERENCES point_accounts(user_id), menu_id BIGINT NOT NULL REFERENCES menus(id), paid_amount BIGINT NOT NULL CHECK (paid_amount > 0), ordered_at TIMESTAMP(6) NOT NULL ); -- 인덱스는 책의 색인처럼 최근 주문을 찾는 작업을 돕습니다. 대신 저장 공간과 쓰기 비용이 듭니다. CREATE INDEX idx_orders_recent ON orders(ordered_at, menu_id); -- Kafka에 아직 보내지 않았거나 확인받지 못한 주문 사건을 잠시 보관합니다. CREATE TABLE order_outbox ( id BIGINT AUTO_INCREMENT PRIMARY KEY, -- 주문 하나에 발행 대기 기록이 중복 생성되지 않게 UNIQUE를 둡니다. order_id BIGINT NOT NULL UNIQUE REFERENCES orders(id), user_id BIGINT NOT NULL, menu_id BIGINT NOT NULL, paid_amount BIGINT NOT NULL, attempts INTEGER NOT NULL DEFAULT 0, next_attempt_at TIMESTAMP(6) NOT NULL ); -- 다음 전송 시간이 된 outbox 행을 빨리 찾기 위한 색인입니다. CREATE INDEX idx_order_outbox_ready ON order_outbox(next_attempt_at, id); -- 처음 실행할 때 사용할 커피 메뉴 네 개를 넣습니다. INSERT INTO menus(name, price) VALUES ('Americano', 4500), ('Cafe Latte', 5000), ('Cappuccino', 5500), ('Cold Brew', 6000); 5. src/main/resources/db/migration/V2__collected_order_events.sql -- Kafka 메시지를 받은 쪽을 흉내 낸 표입니다. 실제 분석 플랫폼의 역할을 실습용 DB로 표현했습니다. CREATE TABLE collected_order_events ( -- 같은 사건이 Kafka에서 두 번 오더라도 같은 event_id는 한 행만 저장합니다. event_id BIGINT PRIMARY KEY, order_id BIGINT NOT NULL, user_id BIGINT NOT NULL, menu_id BIGINT NOT NULL, paid_amount BIGINT NOT NULL, -- 메시지를 받은 시각입니다. 커피가 결제된 시각과는 다릅니다. collected_at TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ); 6. src/main/resources/db/migration/V3__order_idempotency.sql -- 클라이언트가 붙인 결제 시도 이름을 저장합니다. 이미 있던 주문은 이 값이 없어서 NULL을 허용합니다. ALTER TABLE orders ADD COLUMN request_key VARCHAR(128); -- 같은 사용자와 같은 요청 키로 주문을 두 개 만들지 못하게 DB가 막습니다. 서버가 여러 대여도 이 규칙은 공유됩니다. CREATE UNIQUE INDEX uq_orders_user_request_key ON orders(user_id, request_key); 7. src/main/java/com/example/coffee/config/kafka/OrderTopicConfig.java // 이 파일은 config 기능의 kafka 패키지에 속합니다. package com.example.coffee.config.kafka; import org.apache.kafka.clients.admin.NewTopic; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.kafka.config.TopicBuilder; // Spring 설정을 담은 클래스입니다. @Configuration public class OrderTopicConfig { @Bean NewTopic orderPaidTopic(@Value("${orders.topic}") String topic) { // 로컬 Kafka 브로커가 한 대여서 복제본도 한 개입니다. 브로커 장애를 견디는 구성은 아닙니다. // 메시지 저장 줄을 세 개로 나눠 한 그룹에서 최대 세 소비자가 병렬 처리하게 합니다. // 하나의 Kafka 토픽을 세 갈래로 나눠 소비자 세 개가 동시에 처리할 수 있게 합니다. // 브로커가 한 대인 연습 환경이라 복제본은 하나입니다. 장애에 안전한 운영 구성을 뜻하지는 않습니다. return TopicBuilder.name(topic).partitions(3).replicas(1).build(); } } 8. src/main/java/com/example/coffee/order/dto/OrderRequest.java // 이 파일은 order 기능의 dto 패키지에 속합니다. package com.example.coffee.order.dto; import jakarta.validation.constraints.Min; import lombok.Builder; @Builder // 주문할 사람과 메뉴를 담습니다. 금액은 보내지 않고 서버가 메뉴 가격을 조회합니다. public record OrderRequest(@Min(1) long userId, @Min(1) long menuId) {} 9. src/main/java/com/example/coffee/order/dto/OrderEvent.java // 이 파일은 order 기능의 dto 패키지에 속합니다. package com.example.coffee.order.dto; import lombok.Builder; @Builder // Kafka로 보낼 사건입니다. eventId는 같은 메시지가 다시 왔는지 확인하는 번호입니다. public record OrderEvent(long eventId, long orderId, long userId, long menuId, long paidAmount) {} 10. src/main/java/com/ex
A strong brand is built through more than an attractive logo or memorable name. It develops through every interaction a customer has with a business, from a website visit and social media post to packaging, advertising, and customer support. When these experiences feel connected, customers can recognize the brand more easily and understand what it represents. This is why brand consistency has become an important priority for businesses looking to establish a clear and reliable identity. As businesses expand across markets and digital channels, maintaining a consistent identity can become challenging. Professional branding services help organizations create guidelines, messaging, visuals, and strategies that keep the brand recognizable while allowing it to adapt to different audiences. What Does Brand Consistency Mean? Brand consistency means presenting the same core identity across different customer touchpoints. This includes visual elements such as colors, typography, imagery, and logos, as well as communication style, messaging, values, and overall customer experience. Consistency does not mean using identical content everywhere. Instead, it means ensuring that every communication reflects the same underlying personality and positioning. For example, a business may use different messaging on its website, social media, email campaigns, and advertisements. However, the tone, visual identity, and central brand message should remain connected. Why Consistency Matters for Growing Businesses Growing businesses often communicate with customers through multiple platforms. Without clear branding standards, different teams may create content that looks or sounds unrelated. Over time, this can make the business appear less organized and make it harder for customers to identify the brand. A consistent identity can help businesses achieve several objectives: Improve brand recognition Create a professional appearance Strengthen customer familiarity Communicate business values clearly Support a unified marketing strategy Create more predictable customer experiences When customers repeatedly encounter a recognizable identity, they can become more familiar with the business and its offerings. Creating a Recognizable Visual Identity Visual branding is one of the most visible aspects of consistency. Businesses should establish clear rules for using logos, colors, fonts, photography, illustrations, and other design elements. A well-defined visual system makes marketing materials easier to recognize. Whether a customer sees an advertisement, visits a website, or receives an email, familiar visual elements can create a connection between different interactions. Branding services in New York can support businesses in developing cohesive visual systems that work across websites, digital campaigns, presentations, and other communication channels. Maintaining a Consistent Brand Voice Visual consistency is only one part of effective branding. The way a business communicates also influences how customers perceive it. A brand voice defines how the company speaks to its audience. It may be professional, conversational, educational, energetic, or approachable, depending on the business and its audience. Once a suitable voice is established, it should be reflected across important communication channels. This includes website content, social media posts, emails, advertisements, product descriptions, and customer communications. A consistent voice makes communication feel more connected and helps customers develop a clearer understanding of the business. Building Trust Through Familiar Experiences Customers often feel more comfortable with businesses that communicate clearly and consistently. When branding changes dramatically from one platform to another, customers may find it difficult to understand whether the communication represents the same organization. Consistent branding creates familiarity. Over time, familiar colors, language, design patterns, and messaging can make a business easier to identify. Branding services in Los Angeles can help businesses establish structured brand guidelines that support consistency while allowing creative teams to develop fresh campaigns. Supporting Marketing Across Multiple Channels Modern businesses rarely depend on a single marketing channel. They may use search engines, social media, email marketing, websites, paid advertising, events, and offline campaigns to reach customers. Each channel has different requirements, but the underlying brand identity should remain recognizable. For example, a social media campaign may use shorter and more engaging content, while a website may provide detailed information. Although the formats differ, both should communicate the same brand personality and values. This coordinated approach allows different marketing activities to contribute toward one larger brand identity. Making Growth Easier for Internal Teams Brand consistency is also useful internally. As businesses hire new employees, open new locations, or work with different marketing teams, clear brand guidelines provide a common reference point. Guidelines can explain: How the logo should be used Which colors represent the brand What fonts should be selected How images should be presented What tone should be used in written content Which messaging principles should be followed These standards reduce uncertainty and help teams produce materials that remain aligned with the overall brand. Adapting Branding Without Losing Identity Consistency does not mean refusing to change. Growing businesses need to respond to new markets, technologies, customer expectations, and cultural trends. A strong brand system provides flexibility while protecting the elements that make the business recognizable. Branding services in Chicago can help organizations balance creative development with established brand standards, allowing campaigns to evolve without creating an entirely different identity each time. The Long-Term Value of Consistent Branding Brand consistency can contribute to stronger recognition over time. Instead of treating every campaign as an independent project, businesses can build upon an established identity. This creates continuity between past and future marketing activities. It also gives customers repeated opportunities to recognize and understand the brand. For growing organizations, this long-term approach can make marketing more organized and help maintain a clear presence across changing business environments. Conclusion Brand consistency is an important part of building a recognizable and trustworthy business identity. From visual design and brand voice to marketing campaigns and customer interactions, every touchpoint can contribute to how people understand a business. Professional branding services can help organizations establish clear standards while creating enough flexibility for future growth. Whether a business is expanding in New York, Los Angeles, Chicago, or other markets, a consistent brand identity can provide a strong foundation for communication, customer recognition, and sustainable development.
英伟达首席执行官黄仁勋将于当地时间 28 日在纽约再度会见三星电子会长李在镕与 SK 集团会长崔泰源,距三人 7 月旧金山会面时隔约两个月,HBM 是本次会面行业最关注的议题,作为决定英伟达下一代产品性能的核心器件,三星计划集内存、晶圆代工、先进封装于一体切入 AI 芯片市场,双方关系有望从单纯的 HBM 供需拓展至 AI 芯片全流程生产。