<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>cse23_ewha.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Tue, 19 May 2026 09:58:58 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>cse23_ewha.log</title>
            <url>https://velog.velcdn.com/images/cse23_ewha/profile/37bfbdc7-cca0-4dfc-a705-cbc4a8a7dbf8/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. cse23_ewha.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/cse23_ewha" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[잉프라 회고록]]></title>
            <link>https://velog.io/@cse23_ewha/%EC%9E%89%ED%94%84%EB%9D%BC-%ED%9A%8C%EA%B3%A0%EB%A1%9D</link>
            <guid>https://velog.io/@cse23_ewha/%EC%9E%89%ED%94%84%EB%9D%BC-%ED%9A%8C%EA%B3%A0%EB%A1%9D</guid>
            <pubDate>Tue, 19 May 2026 09:58:58 GMT</pubDate>
            <description><![CDATA[<h2 id="1-aws-app-runner가-서울에-없다">1. AWS App Runner가 서울에 없다.</h2>
<p>프로젝트 목적이 &#39;트래픽 스파이크 처리&#39;, &#39;서버리스 지향&#39;, &#39;운영 부담 최소화&#39;
그래서 당연히 EC2보다 관리가 편한 <strong>AWS App Runner</strong>를 쓰기로 결정했고, Role 권한 부여하고 인프라 설계를 어느정도 한상태였는데... 막상 APP runner를 사용해서 배포하려니까 서울 리전(ap-northeast-2)에는 App Runner가 출시조차 안 되어 있었음.........</p>
<h3 id="ai말을-너무-신뢰하지말자">AI말을 너무 신뢰하지말자</h3>
<p>를 다시금 새기게됌</p>
<p>제미나이 응답</p>
<pre><code>1. 도쿄 리전(ap-northeast-1) 사용하기 (적극 추천) ⭐
아시아에서 App Runner를 지원하는장 가까운 리전이 바로 도쿄입니다.

한국(서울)과 도쿄는 지리적으로 가까워서 네트워크 지연 시간(Latency) 차이가 거의 느껴지지 않습니다. 동아리 프로젝트나 테스트용 API 서버로는 전혀 지장이 없습니다.

2. ECS Fargate
프로젝트 목적(트래픽 스파이크 처리, 서버리스 지향, 운영 부담 최소화)에 제일 부합
App Runner가 내부적으로 ECS Fargate 기반이라 사실상 같은 기술을 직접 쓰는 것</code></pre><p>우리의 선택 -&gt; 2번</p>
<h3 id="왜-ecs-fargate">왜 ECS Fargate?</h3>
<p>사실 App Runner도 내부적으로는 ECS Fargate 기반으로 돌아감</p>
<ul>
<li><strong>서버 관리 전무</strong>: EC2처럼 OS 패치나 인프라 관리에 리소스를 쏟을 필요 없이 컨테이너만 관리하면 됌</li>
<li><strong>Auto Scaling &amp; VPC 연동</strong>: CPU/메모리 기반 확장이 자유롭고 VPC 연동이 기본이라 ElastiCache 연동이 자연스러움</li>
<li><strong>기존 자산 유지</strong>: App Runner를 위해 빌드해 둔 <code>Dockerfile</code> → <code>ECR</code> → <code>Task Definition</code> → <code>Service</code> 파이프라인과 GitHub Actions CI/CD 구조를 거의 그대로 가져갈 수 있었음</li>
</ul>
<p>다시 한번 생각해야할것</p>
<h2 id="ai를-너무-신뢰하지는-말자">AI를 너무 신뢰하지는 말자</h2>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Troubleshooting] NIEDU 인프라 이슈 해결 회고]]></title>
            <link>https://velog.io/@cse23_ewha/Troubleshooting-NIEDU-%EC%9D%B8%ED%94%84%EB%9D%BC-%EC%9D%B4%EC%8A%88-%ED%95%B4%EA%B2%B0-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@cse23_ewha/Troubleshooting-NIEDU-%EC%9D%B8%ED%94%84%EB%9D%BC-%EC%9D%B4%EC%8A%88-%ED%95%B4%EA%B2%B0-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Mon, 11 May 2026 04:10:13 GMT</pubDate>
            <description><![CDATA[<h2 id="issue-1-ec2-스토리지-부족으로-인한-배포-중단-및-인스턴스-업그레이드">Issue 1. EC2 스토리지 부족으로 인한 배포 중단 및 인스턴스 업그레이드</h2>
<h3 id="문제-상황-symptom">문제 상황 (Symptom)</h3>
<ul>
<li>새로운 기능을 구현하고 ECR(Elastic Container Registry)에서 최신 도커 이미지를 받아 배포하려 했으나, <strong>&quot;No space left on device&quot;</strong> 에러와 함께 배포가 무한 실패함.</li>
<li>EC2 쉘(Shell)에 접속해 <code>df -h</code>와 <code>docker images</code>를 확인한 결과, AI 모델이 포함된 도커 이미지 용량이 너무 커서 남은 용량이 거의 없었음.</li>
</ul>
<h3 id="원인-분석-cause">원인 분석 (Cause)</h3>
<ul>
<li><strong>AI 이미지의 비대화</strong>: Python AI 서버 이미지가 딥러닝 라이브러리와 모델 파일을 포함하면서 수 GB 단위로 커짐.</li>
<li><strong>인스턴스 자원 한계</strong>: 기존 <code>t3.micro</code> 인스턴스는 디스크 용량뿐만 아니라 메모리(1GB)도 부족하여, 무거운 도커 이미지를 압축 해제하고 실행하는 과정을 버티지 못함.</li>
</ul>
<h3 id="해결-방법-solution">해결 방법 (Solution)</h3>
<ol>
<li><strong>인스턴스 타입 업그레이드</strong>: CPU와 메모리 사양을 높이기 위해 인스턴스를 <code>t3.micro</code>에서 <code>t3.small</code>로 스케일 업(Scale-up)함.</li>
<li><strong>도커 환경 정리</strong>: <code>docker system prune -a</code> 명령어를 통해 사용하지 않는 오래된 이미지와 컨테이너를 삭제하여 가용 공간을 확보함.</li>
<li><strong>EBS 볼륨 최적화</strong>: 인스턴스 타입 변경과 함께 루트 볼륨(EBS) 크기를 증설하여 향후 이미지 업데이트에 대비함.</li>
</ol>
<hr>
<h2 id="issue-2-ai-퀴즈-생성-데이터의-db-저장-정합성-오류">Issue 2. AI 퀴즈 생성 데이터의 DB 저장 정합성 오류</h2>
<h3 id="문제-상황-symptom-1">문제 상황 (Symptom)</h3>
<ul>
<li>Python AI 서버에서 퀴즈 생성 로직은 정상적으로 동작하여 응답을 보내주지만, Spring Boot 서버에서 이를 받아 <strong>DB(<code>Content</code>, <code>Question</code> 테이블 등)에 저장할 때 데이터가 누락되거나 저장되지 않는 현상</strong> 발생.</li>
</ul>
<h3 id="원인-분석-cause-1">원인 분석 (Cause)</h3>
<ol>
<li><strong>API 데이터 정합성 불일치</strong>: Python 서버가 보내주는 JSON 필드 구조와 Spring Boot의 DTO 구조가 미세하게 달러 역직렬화(Deserialization) 과정에서 데이터가 유실됨.</li>
<li><strong>트랜잭션 관리 실패</strong>: 퀴즈 생성 요청은 성공했으나, 연관된 여러 테이블(Step -&gt; Content -&gt; Choice)에 데이터를 넣는 과정에서 하나라도 실패할 경우 전체가 롤백(Rollback)되어야 하는데, 이 처리가 미흡하여 데이터가 꼬임.</li>
<li><strong>영속성 컨텍스트 이슈</strong>: 부모 엔티티가 저장되기 전에 자식 엔티티를 저장하려다 외래 키(FK) 제약 조건 위반이 발생함.</li>
</ol>
<h3 id="해결-방법-solution-1">해결 방법 (Solution)</h3>
<ol>
<li><strong>DTO 및 검증 로직 강화</strong>: Python 서버와의 규약을 재정의하고, <code>@Valid</code>를 통해 들어오는 데이터의 정합성을 최우선으로 검증함.</li>
<li><strong>Cascade 옵션 및 연관관계 편의 메서드 활용</strong>: <code>JPA CascadeType.ALL</code> 설정을 통해 부모 엔티티(<code>Step</code>) 저장 시 자식 엔티티들이 한 번에 안전하게 저장되도록 구조를 개선함.</li>
<li><strong>트랜잭션 원자성 확보</strong>: 퀴즈 저장 서비스 로직에 <code>@Transactional</code>을 적용하여 전체 프로세스가 &quot;All or Nothing&quot;으로 동작하게 하여 데이터 무결성을 보장함.</li>
</ol>
<hr>
<h3 id="회고를-마치며">회고를 마치며</h3>
<blockquote>
<p>&quot;인프라는 한 번 구축하면 끝나는 것이 아니라, 서비스 규모와 데이터 크기에 따라 지속적으로 모니터링하고 스케일링해야 한다는 것을 배웠습니다. 또한, 서로 다른 언어(Spring-Python)로 구성된 서버 간 통신에서는 데이터 정합성을 맞추는 것이 시스템 안정성의 핵심임을 깨달았습니다...어렵다 ㅠㅠㅠ&quot;</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[PROMATE 백엔드 트러블슈팅]]></title>
            <link>https://velog.io/@cse23_ewha/PROMATE-%EB%B0%B1%EC%97%94%EB%93%9C-%ED%8A%B8%EB%9F%AC%EB%B8%94%EC%8A%88%ED%8C%85</link>
            <guid>https://velog.io/@cse23_ewha/PROMATE-%EB%B0%B1%EC%97%94%EB%93%9C-%ED%8A%B8%EB%9F%AC%EB%B8%94%EC%8A%88%ED%8C%85</guid>
            <pubDate>Mon, 04 May 2026 08:52:58 GMT</pubDate>
            <description><![CDATA[<h4 id="1-다중-사용자-환경에서의-데이터-경합race-condition-해결">1. 다중 사용자 환경에서의 데이터 경합(Race Condition) 해결</h4>
<ul>
<li><p><strong>문제 상황:</strong> 여러 명의 팀원이 채팅방에서 동시에 AI 요약(<code>@mates</code>)을 호출하거나 상충하는 데이터를 입력할 때, AI 서버로 중복 요청이 가거나 DB의 요약 데이터가 덮어씌워지는(Lost Update) 데이터 정합성 파괴 현상 발생.</p>
</li>
<li><p><strong>해결 과정:</strong></p>
<ol>
<li><strong>입구 통제 (Redis 분산 락):</strong> AI 호출 시 <code>projectId</code>를 Key로 Redis 분산 락(Distributed Lock)을 걸어, 누군가 요약을 진행 중일 때는 다른 팀원의 중복 호출을 차단함.</li>
<li><strong>출구 방어 (JPA 낙관적 락):</strong> AI 요약본을 DB에 최종 업데이트할 때 <code>@Version</code>을 활용한 낙관적 락(Optimistic Lock)을 적용. 읽은 시점과 쓰는 시점의 버전이 다르면 예외를 발생시키고 안전하게 롤백하여 데이터 오염 방지.</li>
</ol>
</li>
<li><p><strong>결과:</strong> 100%의 데이터 정합성을 보장하는 안전한 실시간 협업 환경 구축 완료.</p>
<pre><code class="language-java">// 1. 엔티티에 낙관적 락 적용 (출구 방어)
@Entity
public class ProjectSummary {
  @Id @GeneratedValue
  private Long id;
  private String content;

  @Version // JPA Optimistic Lock
  private Long version; 
}
</code></pre>
</li>
</ul>
<p>// 2. 분산 락을 활용한 AI 호출 로직 (입구 방어)
@Service
@RequiredArgsConstructor
public class AISummaryService {
    private final RedissonClient redissonClient;
    private final SummaryRepository summaryRepository;</p>
<pre><code>public void requestAiSummary(Long projectId) {
    RLock lock = redissonClient.getLock(&quot;AI_LOCK:&quot; + projectId);
    try {
        // 락 획득 시도 (대기 시간 0초, 락 유지 10초)
        boolean isLocked = lock.tryLock(0, 10, TimeUnit.SECONDS);
        if (!isLocked) {
            throw new CustomException(&quot;이미 AI가 요약을 진행 중입니다.&quot;);
        }

        // AI 호출 및 DB 업데이트 로직 (낙관적 락 충돌 시 ObjectOptimisticLockingFailureException 발생)
        updateSummaryWithAI(projectId);

    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    } finally {
        if (lock.isLocked() &amp;&amp; lock.isHeldByCurrentThread()) {
            lock.unlock(); // 작업 완료 후 락 해제
        }
    }
}</code></pre><p>}</p>
<pre><code>
#### 2. 다중 서버(Scale-out) 환경에서의 WebSocket 세션 동기화 문제
* **문제 상황:** 단일 서버에서는 STOMP 기반 채팅이 잘 동작했으나, 트래픽 분산을 위해 백엔드 서버를 여러 대(Scale-out)로 늘렸을 때 서로 다른 서버에 접속한 유저 간에 메시지가 전달되지 않는 브로드캐스팅 단절 문제 발생.
* **해결 과정:** * **Redis Pub/Sub 도입:** 기존에 세션 관리용으로 쓰던 Redis의 Pub/Sub(발행/구독) 기능을 메세지 브로커로 확장 활용.
    * A서버의 유저가 메시지를 보내면 Redis로 Publish하고, Redis가 연결된 모든 서버(B, C 등)로 메시지를 브로드캐스팅하여, 어떤 서버에 붙어있든 실시간 통신이 끊기지 않도록 분산 웹소켓 아키텍처 재설계.
* **결과:** 대규모 접속 시에도 안정적인 채팅이 가능한 확장성(Scalability) 확보.

```java
// 1. 메시지 발행 (Publish)
@MessageMapping(&quot;/chat/message&quot;)
public void sendMessage(ChatMessage message) {
    // DB에 메시지 선 저장 (단일 진실 공급원)
    chatService.saveMessage(message);
    // Redis의 채팅 채널로 메시지 발행
    redisTemplate.convertAndSend(&quot;CHAT_ROOM:&quot; + message.getProjectId(), message);
}

// 2. 메시지 구독 (Subscribe) - RedisMessageListenerContainer 설정
@Service
public class RedisSubscriber implements MessageListener {
    private final SimpMessageSendingOperations messagingTemplate;

    @Override
    public void onMessage(Message message, byte[] pattern) {
        // Redis에서 메시지를 수신하면, 자신(서버)에게 연결된 웹소켓 클라이언트들에게 브로드캐스팅
        ChatMessage chatMessage = objectMapper.readValue(message.getBody(), ChatMessage.class);
        messagingTemplate.convertAndSend(&quot;/sub/chat/room/&quot; + chatMessage.getProjectId(), chatMessage);
    }
}
</code></pre><h4 id="3-외부-apinotion-rate-limit호출-제한-및-트랜잭션-병목-대응">3. 외부 API(Notion) Rate Limit(호출 제한) 및 트랜잭션 병목 대응</h4>
<ul>
<li><strong>문제 상황:</strong> 프로젝트 완료 후 Notion으로 템플릿을 내보낼 때, 다수의 팀이 동시에 요청할 경우 Notion API의 엄격한 호출 횟수 제한(429 Too Many Requests)에 걸려 실패. 게다가 동기식으로 API를 기다리다가 우리 DB 커넥션 풀까지 고갈될 위험 감지.</li>
<li><strong>해결 과정:</strong><ol>
<li><strong>트랜잭션 분리:</strong> 우리 DB에 데이터를 안전하게 커밋(Commit)하여 완전히 저장한 <strong>이후에만</strong> 외부 API(Notion)를 호출하도록 설계하여, 외부 장애가 내부 시스템으로 번지지 않도록 격리.</li>
<li><strong>비동기 큐 및 백오프:</strong> Redis 작업 큐(Task Queue)를 도입해 쏟아지는 노션 생성 요청을 담아두고, 워커(Worker)가 속도를 조절하며 순차 처리. API 에러 발생 시 대기 시간을 점진적으로 늘리는 &#39;지수 백오프(Exponential Backoff)&#39; 적용 및 실패 시 사용자 재시도(Retry) 로직 구현.</li>
</ol>
</li>
<li><strong>결과:</strong> 외부 API 장애로부터 내결함성(Fault Tolerance)을 갖춘 견고한 시스템 완성.<pre><code class="language-java">// 1. DB 트랜잭션과 외부 API 분리 (@TransactionalEventListener 활용)
@Service
public class ProjectService {
  @Transactional
  public void finishProject(Long projectId) {
      projectRepository.updateStatus(projectId, &quot;COMPLETED&quot;);
      // DB 커밋이 완료된 후 이벤트 발행
      eventPublisher.publishEvent(new ProjectCompletedEvent(projectId));
  }
}
</code></pre>
</li>
</ul>
<p>// 2. 이벤트 리스너에서 비동기로 Notion API 호출 및 재시도 로직 적용
@Component
public class NotionApiEventListener {</p>
<pre><code>// 트랜잭션이 성공적으로 커밋된 후에만 실행됨
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
@Async // 비동기 워커로 넘김 (API 병목 방지)
// 429 에러 발생 시 1초, 2초, 4초 대기하며 최대 3번 재시도
@Retryable(value = {RateLimitException.class}, maxAttempts = 3, backoff = @Backoff(delay = 1000, multiplier = 2))
public void handleProjectCompletedEvent(ProjectCompletedEvent event) {
    notionApiClient.exportToNotion(event.getProjectId());
}

// 최종 실패 시 처리 (Fallback)
@Recover
public void recoverNotionExport(RateLimitException e, ProjectCompletedEvent event) {
    log.error(&quot;Notion API 최대 재시도 횟수 초과. 프로젝트 ID: {}&quot;, event.getProjectId());
    // DB에 상태를 &#39;FAILED&#39;로 기록하고 사용자에게 &#39;재시도 버튼&#39; 노출 유도
    projectRepository.updateExportStatus(event.getProjectId(), &quot;FAILED&quot;);
}</code></pre><p>}</p>
<pre><code>#### 4. AI 환각(Hallucination) 및 비정형 JSON 응답에 대한 서버 생존성 확보
* **문제 상황:** 프롬프트로 JSON 형식을 강제했음에도, AI가 가끔 규격에 맞지 않는 엉뚱한 응답을 보내거나 정의되지 않은 Key를 던질 때 백엔드 서버에서 파싱 에러(Mapping Exception)가 발생하며 프로세스가 중단됨.
* **해결 과정:**
    * **방어적 프로그래밍과 Fallback:** Spring Boot 단에서 철저한 객체 검증(DTO Validation) 및 Try-Catch 예외 처리 적용. 에러 포착 시 서버를 멈추지 않고(Graceful Degradation), 사용자에게 알림을 보낸 뒤 **&#39;가장 최근에 검증된 성공 데이터(Last Known Good State)&#39;를 DB에 유지(Fallback)**하여 데이터 유실 방지.
* **결과:** AI의 실수에도 시스템이 무너지지 않고 사용자 데이터를 100% 보호하는 안전장치 마련.

```java
@Service
public class AiResponseHandler {

    // JSON에 정의되지 않은 필드가 들어와도 무시하도록 ObjectMapper 설정
    private final ObjectMapper objectMapper = new ObjectMapper()
            .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

    @Transactional
    public void processAiResponse(Long projectId, String aiRawResponse) {
        ProjectSummary currentSummary = summaryRepository.findByProjectId(projectId);

        try {
            // AI 응답 파싱 시도
            AiSummaryDto dto = objectMapper.readValue(aiRawResponse, AiSummaryDto.class);

            // 검증 성공 시에만 데이터 업데이트
            currentSummary.updateContent(dto.getContent());

        } catch (JsonProcessingException e) {
            // 파싱 실패 시 서버를 죽이지 않고 로그만 남김 (Graceful Degradation)
            log.error(&quot;AI 응답 JSON 파싱 실패. Raw Data: {}&quot;, aiRawResponse, e);

            // TODO: 슬랙 등 모니터링 채널로 알림 발송

            // 기존 데이터(currentSummary)는 롤백하지 않고 그대로 유지됨 (안전한 Fallback)
        }
    }
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[ACC 핸즈온세션3]]></title>
            <link>https://velog.io/@cse23_ewha/ACC-%ED%95%B8%EC%A6%88%EC%98%A8%EC%84%B8%EC%85%983</link>
            <guid>https://velog.io/@cse23_ewha/ACC-%ED%95%B8%EC%A6%88%EC%98%A8%EC%84%B8%EC%85%983</guid>
            <pubDate>Tue, 21 Apr 2026 12:13:08 GMT</pubDate>
            <description><![CDATA[<h3 id="데이터베이스db-및-dbms">데이터베이스(DB) 및 DBMS</h3>
<ul>
<li><strong>데이터베이스(DB)</strong> 
   구조화된 정보 또는 데이터의 조직화된 모음이며, 일반적으로 DBMS에 의해 제어됨</li>
<li><strong>관계형 데이터베이스(Relational DB)</strong> 
   구조화된 데이터를 테이블 형태로 저장
   엄격한 스키마를 따르며 SQL(Structured Query Language)을 사용하여 데이터를 조작</li>
<li><strong>비관계형 데이터베이스(Non-Relational DB)</strong>
   비정형 데이터를 저장하며 고정된 스키마가 없음
   대량의 분산 데이터 저장 및 다양한 형태의 데이터를 빠르게 처리하는 데 적합함</li>
</ul>
<h3 id="rds의-배포-방식-비교">RDS의 배포 방식 비교</h3>
<ul>
<li><strong>On-Premise:</strong> 사용자가 직접 물리적 서버를 구축하고 데이터베이스를 설치 및 관리</li>
<li><strong>AWS EC2:</strong> AWS 가상 서버(EC2) 위에 사용자가 직접 DB 소프트웨어를 설치하고 운영 체제부터 DB까지 관리</li>
<li><strong>AWS RDS:</strong> AWS에서 제공하는 <strong>완전 관리형</strong> 관계형 데이터베이스 서비스로, 관리 부담이 가장 낮음</li>
</ul>
<h3 id="aws-rds-relational-database-service">AWS RDS (Relational Database Service)</h3>
<ul>
<li><strong>개요:</strong> AWS 클라우드에서 관계형 데이터베이스를 쉽게 설치, 운영 및 확장할 수 있도록 지원하는 웹 서비스</li>
<li><strong>주요 장점:</strong><ul>
<li><strong>간편한 관리:</strong> 패치, 백업 등 운영 자동화</li>
<li><strong>가용성 및 안정성:</strong> 자동 백업 및 스냅샷 기능을 제공하며 Multi-AZ를 통한 장애 복구 지원</li>
<li><strong>보안성:</strong> AWS 네트워크 보안 기능(VPC) 및 암호화 적용 가능</li>
<li><strong>확장성:</strong> Read Replica를 통한 읽기 성능 확장 및 스토리지 유연성 확보</li>
<li><strong>비용 효율성:</strong> 사용한 만큼만 비용을 지불하는 구조</li>
</ul>
</li>
</ul>
<h3 id="가용성-및-성능-확장-기술">가용성 및 성능 확장 기술</h3>
<ul>
<li>Multi-AZ (다중 가용 영역):<ul>
<li>다른 가용 영역(AZ)에 데이터베이스 복사본(Standby)을 생성하고 데이터를 동기화함</li>
<li>장애 감지 시 자동으로 대기 인스턴스로 대체(Failover)되어 서비스 연속성을 보장함</li>
</ul>
</li>
<li>Read Replica (읽기 전용 복제본):<ul>
<li>읽기(Read) 쿼리의 성능 향상과 부하 분산을 목적으로 생성함</li>
<li>주 인스턴스로부터 비동기 방식으로 데이터를 복제함</li>
<li>읽기 중심 워크로드에서 Scale-Out을 통해 처리량을 향상시킴</li>
</ul>
</li>
</ul>
<p>SAA (Solutions Architect Associate) 실무 사례</p>
<ul>
<li>문제 상황: RDS 사용 중 통계용 데이터를 읽어올 때 성능 저하가 발생하는 경우</li>
<li>해결책: RDS Read Replica를 생성하여 운영 DB의 부하를 줄이고, 읽기 전용 쿼리를 복제본에서 처리하도록 구성!!</li>
</ul>
<h3 id="sql-및-관계형-데이터베이스rdbms">SQL 및 관계형 데이터베이스(RDBMS)</h3>
<ul>
<li>관계형 데이터베이스에서 데이터를 저장, 조회, 수정, 삭제하기 위해 사용하는 언어</li>
<li><strong>특징:</strong><ul>
<li>정해진 구조(Schema)에 따라 테이블에 데이터를 저장</li>
<li>행(Row)과 열(Column)이 있는 표 형태의 구조를 가짐</li>
</ul>
</li>
<li>Oracle, MySQL, PostgreSQL 등</li>
</ul>
<h3 id="nosql-및-비관계형-데이터베이스">NoSQL 및 비관계형 데이터베이스</h3>
<ul>
<li>비관계형 데이터베이스 환경에서 데이터를 관리하는 방식임</li>
<li>특징:<ul>
<li>정형화되지 않은 유연한 구조를 사용하여 가용성과 확장성이 높음</li>
<li>고성능 처리에 최적화되어 있으며 SNS(Instagram, Facebook) 등에서 주로 사용됨</li>
</ul>
</li>
<li>MongoDB, AWS DynamoDB 등</li>
</ul>
<h3 id="aws-dynamodb-개요">AWS DynamoDB 개요</h3>
<ul>
<li>AWS에서 제공하는 서버리스 기반의 완전관리형 NoSQL 데이터베이스 서비스</li>
<li>주요 특성:<ul>
<li><strong>완전관리형:</strong> 장비 운영부터 솔루션 설치 및 운영까지 모든 인프라를 AWS에서 담당</li>
<li><strong>고성능:</strong> 10ms 미만의 지연 시간으로 데이터를 처리할 만큼 매우 빠름</li>
<li><strong>내구성:</strong> SSD에 저장되며 리전 내 여러 가용 영역(AZ)에 걸쳐 자동 복제</li>
<li><strong>Auto-Scaling:</strong> 요청량에 따라 테이블의 읽기/쓰기 용량을 자동으로 조절</li>
</ul>
</li>
</ul>
<h3 id="dynamodb-핵심-구성-요소">DynamoDB 핵심 구성 요소</h3>
<ul>
<li><strong>테이블(Tables)</strong>
데이터의 집합체 (예: People 테이블, Cars 테이블)</li>
<li><strong>항목(Items)</strong> 
테이블을 구성하는 개별 데이터 단위 (RDBMS의 행에 해당)</li>
<li><strong>속성(Attributes)</strong>
항목을 구성하는 세부 데이터 요소 (예: ID, 이름, 주소 등)</li>
<li><strong>유연한 스키마</strong> 
기본 키(PK)를 제외하고는 속성이나 데이터 형식을 미리 정의할 필요가 없으며, 각 항목은 고유하거나 중첩된 속성을 가질 수 있음</li>
</ul>
<h3 id="dynamodb의-키key-구조">DynamoDB의 키(Key) 구조</h3>
<ul>
<li><strong>Partition Key (파티션 키):</strong><ul>
<li>RDBMS의 Primary Key와 유사한 역할로 테이블 내 유일한 값이어야 함</li>
<li>데이터가 저장될 물리적 공간(Partition)을 결정하며 일치 검색(Equal)만 지원</li>
</ul>
</li>
<li><strong>Sort Key (정렬 키):</strong><ul>
<li>같은 파티션 내에서 데이터를 정렬하는 기준</li>
<li>일치, 부등호, 포함 등 범위 지정 검색을 지원하여 정교한 쿼리를 가능하게 함</li>
</ul>
</li>
</ul>
<h3 id="lambda를-활용한-dynamodb-최적화">Lambda를 활용한 DynamoDB 최적화</h3>
<ul>
<li><strong>Batch 작성 지원</strong>
<code>BatchWriteItem API</code>를 통해 한 번에 최대 25개의 항목을 삽입할 수 있어 데이터 추가 효율이 높음</li>
<li><strong>자동화 운용</strong> 
CloudWatch Events와 연동하여 특정 주기마다 Lambda 함수를 실행, 정기적인 데이터 처리가 가능</li>
<li><strong>Join 기능 구현</strong> 
DynamoDB는 자체적인 Join을 지원하지 않으므로, 복잡한 데이터 결합이 필요한 경우 Lambda를 통해 로직으로 구현</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[ACC 핸즈온세션2]]></title>
            <link>https://velog.io/@cse23_ewha/ACC-%ED%95%B8%EC%A6%88%EC%98%A8%EC%84%B8%EC%85%982</link>
            <guid>https://velog.io/@cse23_ewha/ACC-%ED%95%B8%EC%A6%88%EC%98%A8%EC%84%B8%EC%85%982</guid>
            <pubDate>Tue, 07 Apr 2026 13:11:31 GMT</pubDate>
            <description><![CDATA[<h3 id="1-클라우드-스토리지-cloud-storage-개요"><strong>1. 클라우드 스토리지 (Cloud Storage) 개요</strong></h3>
<ul>
<li><strong>정의:</strong> 클라우드 컴퓨팅 제공업체를 통해 데이터와 파일을 인터넷에 저장하는 모델. 사용자는 퍼블릭 인터넷 또는 전용 프라이빗 네트워크 연결을 통해 스토리지에 액세스 가능.</li>
<li><strong>클라우드 사용 시 이점:</strong> 비용 효율성, 민첩성 향상, 더 빠른 배포, 효율적인 데이터 관리, 확장성</li>
<li><strong>스토리지 종류:</strong> File Storage, Block Storage, Object Storage (사용자가 데이터를 어떻게 읽고 쓰는지 인터페이스 관점에서 구분)</li>
</ul>
<h3 id="2-스토리지-유형별-상세-분석"><strong>2. 스토리지 유형별 상세 분석</strong></h3>
<h4 id="①-block-storage-예-amazon-ebs"><strong>① Block Storage (예: Amazon EBS)</strong></h4>
<ul>
<li><strong>역할:</strong> 물리적 하드웨어(HDD, SSD)와 비슷한 역할을 수행하며 이를 흉내 냄. 가상머신(EC2)은 이를 자신의 하드디스크라고 생각함</li>
<li><strong>특징:</strong> 스토리지 어디에 저장되어 있는지 등의 세부 작업은 직접 하지 못함(물리적 디바이스 관리는 OS가 담당)</li>
<li><strong>데이터 처리:</strong> 데이터를 <strong>Block 형태</strong>로 Read/Write 함. 빠른 속도를 위해 각 블록에 <strong>고유 식별자(ID#)</strong>를 부여</li>
<li><strong>기타:</strong> 백업 기능을 제공하기도 하며 SAN(Storage Area Network) 역할을 수행</li>
</ul>
<h4 id="②-file-storage-예-amazon-efs"><strong>② File Storage (예: Amazon EFS)</strong></h4>
<ul>
<li><strong>역할:</strong> 데이터를 파일 단위의 계층 구조로 저장함. 애플리케이션에 가장 널리 사용되는 유형</li>
<li><strong>특징:</strong> 데이터를 파일 단위로 다루며 직접 데이터를 관리함(서버 기능도 수행). <strong>NFS, NAS 기술</strong> 사용 가능함. 로컬 하드 드라이브와 유사한 접근 방식을 가짐</li>
<li><strong>사용 시나리오:</strong> 블록 스토리지에 &#39;서버&#39;가 추가된 느낌으로, 여러 서버나 사용자가 동일 파일 시스템에 동시 접근하거나 네트워크를 통한 파일 공유가 필요할 때 사용</li>
</ul>
<h4 id="③-object-storage-예-amazon-s3"><strong>③ Object Storage (예: Amazon S3)</strong></h4>
<ul>
<li><strong>역할:</strong> 대용량 미디어 파일, 이미지, 백업 등 <strong>비정형 데이터</strong> 저장을 위한 스토리지</li>
<li><strong>특징:</strong> 데이터를 <strong>오브젝트(파일+메타데이터)</strong> 단위로 다룸. 전송된 형식 그대로 객체 데이터로 저장하며, 사용자가 직접 메타데이터 지정 가능</li>
<li><strong>접근 방식:</strong> 객체는 &#39;보안 버킷&#39;이라는 공간에 저장되며, <strong>HTTP 프로토콜 기반 REST API 호출</strong>을 통해 접근<ul>
<li><em>REST API란? HTTP의 장점을 살려 자원을 이름으로 구분하고 상태를 주고받는 API 방식.</em></li>
</ul>
</li>
<li><strong>사용 시나리오:</strong> 정적 웹 콘텐츠 호스팅, 전 세계 데이터센터 분산, 저렴한 비용으로 장기 보관 시 유리</li>
</ul>
<h3 id="3-amazon-s3-simple-storage-service-심화"><strong>3. Amazon S3 (Simple Storage Service) 심화</strong></h3>
<ul>
<li><strong>주요 특징:</strong> 확장성이 뛰어나 저장 용량을 신경 쓸 필요가 없으며, 버전 관리/암호화/복제 기능으로 데이터를 보호. 사용한 만큼만 비용을 지불</li>
<li><strong>구성 요소:</strong><ul>
<li><strong>버킷(Bucket):</strong> 최상위 디렉토리(컨테이너). 폴더와 유사함. 무제한 객체 저장 가능. 계정당 최대 100개 생성 가능. 전 세계에서 유일한 이름을 가져야 함</li>
<li><strong>객체(Object):</strong> 버킷 안의 파일. 크기는 최대 5TB. <strong>Key(이름)와 버전 ID</strong>로 식별됨.</li>
</ul>
</li>
<li><strong>객체의 상세 구성:</strong> Key(이름), Value(데이터), Version ID, Metadata(수정일, 타입, 소유자, 사이즈), 태그, 액세스 제어 정보.</li>
<li><strong>태그 활용 보안:</strong> * <code>s3:ExistingObjectTag</code>: 기존 객체에 특정 태그가 있는지 확인.<ul>
<li><code>s3:RequestObjectTagKeys</code>: 허용할 태그 키 제한</li>
<li><code>s3:RequestObjectTag</code>: 허용할 태그 키 및 값 제한</li>
<li><em>예시: environment: production 태그가 있는 객체만 읽기 허용 가능.</em></li>
</ul>
</li>
<li><strong>URL 형식 예시:</strong> <code>https://awsinaction.s3.ap-southeast-2.amazonaws.com/img/cat.png</code><ul>
<li>버킷명: <code>awsinaction</code> / 리전: <code>ap-southeast-2</code> / 키네임: <code>img/cat.png</code></li>
</ul>
</li>
</ul>
<h3 id="4-s3-보안-및-관리-기능"><strong>4. S3 보안 및 관리 기능</strong></h3>
<h4 id="데이터-암호화"><strong>데이터 암호화</strong></h4>
<ul>
<li><strong>서버 측 암호화 (Server-Side Encryption):</strong> AWS가 자동으로 보호. 업로드 시 암호화, 다운로드 시 복호화<ul>
<li>종류: SSE-S3(기본), SSE-KMS, DSSE-KMS, SSE-C. </li>
<li>요청 헤더 예시: <code>&quot;x-amz-server-side-encryption&quot;: &quot;AES256&quot;</code></li>
</ul>
</li>
<li><strong>클라이언트 측 암호화:</strong> 내 로컬 컴퓨터에서 암호화 후 업로드. 제3자에게 노출을 원천 방지하나 관리 복잡성이 높음</li>
</ul>
<h4 id="액세스-제어-access-control"><strong>액세스 제어 (Access Control)</strong></h4>
<ul>
<li>기본값은 <strong>Private</strong>임 (통째로 퍼블릭 설정 안 됨)</li>
<li><strong>IAM:</strong> 사용자/역할 기준. 내 계정 내 유저가 무엇을 할 수 있는지 제어</li>
<li><strong>ACL:</strong> 계정 단위 기준. 다른 AWS 계정에 대해 읽기/쓰기 권한 부여</li>
<li><strong>버킷 정책:</strong> 버킷/객체 기준. 단일 버킷 내 모든 객체 권한을 세부 구성(IP나 도메인 제한 가능).</li>
<li><strong>CORS:</strong> 출처가 다른 서버 간의 리소스 공유를 허용(Access-Control-Allow-Origin 헤더 사용).</li>
<li><strong>Pre-signed URL:</strong> 임시 URL 발급. 특정 시간 동안만 권한 없는 사용자에게 액세스 부여. 생성자가 유효한 권한을 보유해야 함.</li>
</ul>
<h4 id="스토리지-관리"><strong>스토리지 관리</strong></h4>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/787e9672-7c31-49de-9585-d34369b2a16d/image.png" alt=""></p>
<ul>
<li><strong>버전 관리:</strong> 업데이트 시 버전이 추가됨. 실수로 삭제/덮어쓰기 시 복원 가능. 한 번 활성화 시 비활성화 불가.</li>
<li><strong>객체 복제:</strong> 동일 리전(SRR) 또는 타 리전(CRR)에 비동기 자동 복제. 배치 복제는 기존 객체 복제 시 사용.</li>
<li><strong>스토리지 클래스:</strong> 요구사항에 맞는 클래스 선택 가능.</li>
<li><strong>수명 주기(Lifecycle):</strong><ul>
<li><strong>전환 작업:</strong> 일정 기간 후 저렴한 클래스로 이동 (30일 뒤 IA, 60일 뒤 Glacier 등).</li>
<li><strong>만료 작업:</strong> 일정 기간 후 객체 자동 삭제.</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/26e13486-8fcc-4b4c-98d8-e37623ce0745/image.png" alt=""></p>
<h3 id="5-amazon-cloudfront-cdn"><strong>5. Amazon CloudFront (CDN)</strong></h3>
<ul>
<li><strong>정의:</strong> 글로벌 콘텐츠 전송 네트워크 서비스. 짧은 지연 시간과 빠른 속도로 데이터 전송. S3 앞단에서 보안 기능 제공</li>
<li><strong>CDN 원리:</strong> 여러 서버에 데이터를 분산 캐싱하여 사용자에게 가장 가까운 서버에서 배포</li>
<li><strong>주요 인프라:</strong><ul>
<li><strong>Edge Location:</strong> 원본 서버의 데이터를 캐싱하여 사용자에게 제공하는 지리적 거점</li>
<li><strong>Regional Edge Caches(REC):</strong> 오리진과 엣지 사이의 캐시 계층. 엣지에 없으면 REC를 먼저 확인하여 오리진 부하를 줄임.</li>
</ul>
</li>
<li><strong>동작 방식:</strong> 요청 → 엣지 확인 → 캐시 있으면 응답 / 없으면 오리진 포워딩 → 엣지 캐싱 후 응답.</li>
<li><strong>Origin Server:</strong> S3(정적) 및 EC2/ELB(동적) 연결 가능. 동적 콘텐츠는 TTL 동안 수정사항이 안 보일 수 있으므로 주의 필요.</li>
<li><strong>보안 및 전송 최적화:</strong><ul>
<li><strong>HTTPS 지원:</strong> 오리진이 지원 안 해도 CloudFront에서 설정 가능.</li>
<li><strong>지리적 제한:</strong> 특정 지역 접근 제한 가능.</li>
<li><strong>Signed URL:</strong> 특정 파일 하나에 대해 허용된 사용자만 접근 허용 (유료 결제 등)</li>
<li><strong>Signed Cookie:</strong> 다수의 파일에 대한 액세스 제공 (로그인한 유료 회원 등)</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[ACC 과제2 - AWS Storage 실습 가이드]]></title>
            <link>https://velog.io/@cse23_ewha/ACC-%EA%B3%BC%EC%A0%9C2-AWS-Storage-%EC%8B%A4%EC%8A%B5-%EA%B0%80%EC%9D%B4%EB%93%9C</link>
            <guid>https://velog.io/@cse23_ewha/ACC-%EA%B3%BC%EC%A0%9C2-AWS-Storage-%EC%8B%A4%EC%8A%B5-%EA%B0%80%EC%9D%B4%EB%93%9C</guid>
            <pubDate>Tue, 07 Apr 2026 12:58:05 GMT</pubDate>
            <description><![CDATA[<h3 id="1-사전-준비"><strong>1. 사전 준비</strong></h3>
<ul>
<li><strong>빌드 파일 준비:</strong> 배포할 웹 프로젝트의 빌드 파일을 다운로드하고 압축을 해제</li>
</ul>
<h3 id="2-amazon-s3-버킷-생성-및-설정"><strong>2. Amazon S3 버킷 생성 및 설정</strong></h3>
<ol>
<li><strong>S3 콘솔 접속:</strong> [버킷 만들기] 버튼을 클릭</li>
<li><strong>리전 및 이름 설정:</strong><ul>
<li><strong>리전:</strong> 사용자 데이터 지연 시간 단축과 비용 절감을 위해 지리적으로 가까운 리전(예: 서울)을 선택</li>
<li><strong>이름:</strong> 전역적으로 고유한 이름을 지정 (중복 불가)</li>
</ul>
</li>
<li><strong>생성 완료:</strong> 그 외 설정은 기본값을 유지하고 [버킷 만들기]를 완료</li>
</ol>
<h3 id="3-정적-파일-업로드"><strong>3. 정적 파일 업로드</strong></h3>
<ol>
<li><strong>파일 업로드:</strong> [업로드] 버튼을 클릭</li>
<li><strong>주의사항:</strong> 빌드 폴더 자체를 올리는 것이 아니라, <strong>빌드 폴더 내부에 있는 개별 파일 및 폴더들</strong>을 선택하여 업로드 (예: <code>index.html</code>, <code>static/</code> 등)</li>
</ol>
<h3 id="4-s3-정적-웹사이트-호스팅-활성화"><strong>4. S3 정적 웹사이트 호스팅 활성화</strong></h3>
<ol>
<li><strong>호스팅 설정:</strong> 버킷 상단 <strong>[속성]</strong> 탭 → 최하단 <strong>[정적 웹사이트 호스팅]</strong> 편집 클릭</li>
<li><strong>활성화:</strong> * <strong>인덱스 문서:</strong> <code>index.html</code> 입력<ul>
<li><strong>오류 문서:</strong> <code>index.html</code> 입력 (SPA 라우팅 대응)</li>
</ul>
</li>
<li>저장 후 생성된 <strong>엔드포인트</strong>로 접속<ul>
<li><strong>403 Forbidden</strong> 에러 발생 (정상) </li>
<li>보안을 위해 외부 접근이 막혀 있으므로 <strong>CloudFront</strong>를 사용해야함</li>
</ul>
</li>
</ol>
<h3 id="5-cloudfront-배포-생성-cdn-설정"><strong>5. CloudFront 배포 생성 (CDN 설정)</strong></h3>
<ol>
<li><strong>CloudFront 콘솔 접속:</strong> [배포 생성] 버튼을 클릭</li>
<li><strong>원본(Origin) 설정:</strong><ul>
<li><strong>오리진 도메인:</strong> 앞서 생성한 S3 버킷을 선택</li>
<li><strong>원본 액세스:</strong> <strong>원본 액세스 제어 설정(OAC, 권장)</strong>을 선택</li>
<li><strong>OAC 생성:</strong> [Create new OAC] 버튼 클릭 → 기본 설정 유지 후 [Create] 클릭</li>
</ul>
</li>
<li><strong>보안 및 기타 설정:</strong><ul>
<li><strong>WAF:</strong> 보안 보호 비활성화 선택 (실습용이니)</li>
<li><strong>기본값 루트 객체:</strong> <code>index.html</code> 입력</li>
</ul>
</li>
<li><strong>배포 완료:</strong> [배포 생성]을 누르면 도메인이 생성됌<ul>
<li>하지만 여전히 접속 안 됨! CloudFront가 S3에 접근할 권한이 아직 없기 때문..</li>
</ul>
</li>
</ol>
<h3 id="6-s3-버킷-정책-수정-권한-부여"><strong>6. S3 버킷 정책 수정 (권한 부여)</strong></h3>
<ol>
<li><strong>정책 복사:</strong> * CloudFront 콘솔 → 생성한 배포 선택 → <strong>[원본]</strong> 탭 → 원본 선택 후 <strong>[편집]</strong><ul>
<li><strong>[정책 복사]</strong> 버튼을 클릭하여 CloudFront가 생성해준 정책 문구(JSON)를 복사</li>
</ul>
</li>
<li><strong>S3에 정책 적용:</strong><ul>
<li>S3 콘솔 접속 → 해당 버킷 선택 → <strong>[권한]</strong> 탭</li>
<li><strong>[버킷 정책]</strong> 편집 버튼 클릭 → 복사한 내용을 붙여넣고 저장</li>
</ul>
</li>
<li>이제 CloudFront 배포 도메인으로 접속하면 웹사이트가 정상적으로 나타남</li>
</ol>
<hr>
<h4 id="왜-이렇게-복잡하게-하니"><strong>왜 이렇게 복잡하게 하니?</strong></h4>
<ul>
<li><strong>S3 단독 호스팅:</strong> 보안에 취약하며 속도가 느릴 수 있음</li>
<li><strong>CloudFront 병행:</strong> </li>
</ul>
<ol>
<li>보안 - S3를 외부에 직접 공개하지 않고 CloudFront만 통하게 하여 데이터를 보호 (OAC 설정의 이유)</li>
<li>속도 - 전 세계 Edge Location에 콘텐츠를 캐싱하여 사용자에게 더 빠르게 전달</li>
<li>비용 - 캐싱을 통해 S3 직접 요청 횟수를 줄여 비용을 최적화</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[ACC 과제1 - AWS VPC 기본 실습 가이드]]></title>
            <link>https://velog.io/@cse23_ewha/ACC-%EA%B3%BC%EC%A0%9C1-AWS-VPC-%EA%B8%B0%EB%B3%B8-%EC%8B%A4%EC%8A%B5-%EA%B0%80%EC%9D%B4%EB%93%9C</link>
            <guid>https://velog.io/@cse23_ewha/ACC-%EA%B3%BC%EC%A0%9C1-AWS-VPC-%EA%B8%B0%EB%B3%B8-%EC%8B%A4%EC%8A%B5-%EA%B0%80%EC%9D%B4%EB%93%9C</guid>
            <pubDate>Wed, 01 Apr 2026 13:19:03 GMT</pubDate>
            <description><![CDATA[<h4 id="step-1-vpc-생성-및-설정"><strong>Step 1. VPC 생성 및 설정</strong></h4>
<ul>
<li><strong>작업:</strong> 리전 선택(서울) → VPC 생성 (이름 태그 설정)</li>
<li><strong>설정:</strong> DNS 확인 및 DNS 호스트이름 활성화 체크</li>
<li>❓ IPv4 CIDR를 왜 수동으로 입력?<ul>
<li>VPC는 나만의 독립적인 가상 네트워크 영토를 선언하는 것. 수동으로 입력하는 이유는 내가 관리할 네트워크의 크기와 주소 범위를 직접 결정하기 위해서!!</li>
<li>특히 기업 환경에서는 다른 VPC나 회사 내부망(On-premise)과 연결할 때 주소가 겹치면 통신이 불가능함. 따라서 나중에 확장성이나 연결성을 고려하여 겹치지 않는 적절한 크기(예: <code>/16</code>)를 직접 지정해야하기 때문에 수동으로 입력</li>
</ul>
</li>
</ul>
<h4 id="step-2-public-및-private-서브넷-생성"><strong>Step 2. Public 및 Private 서브넷 생성</strong></h4>
<ul>
<li><strong>VPC 범위:</strong> <code>10.0.0.0/16</code> </li>
<li><strong>서브넷 구성:</strong> 2a, 2c 가용 영역에 각각 퍼블릭/프라이빗 배치</li>
</ul>
<ol>
<li>public subnet : 10.0.1.0/24 -&gt; ap-northeast-2a</li>
<li>private subnet : 10.0.2.0/25-&gt; ap-northeast-2a</li>
<li>public subnet : 10.0.3.0/24  -&gt; ap-northeast-2c</li>
<li>private subnet : 10.0.4.0/25 -&gt; ap-northeast-2c</li>
</ol>
<ul>
<li>❓ 왜 Public이 Private보다 더 많은 주소를 할당?<ul>
<li>실무에서는 사실 반대인 경우가 더 많긴함. 보통 DB나 내부 로직이 돌아가는 Private 영역에 서버가 훨씬 많이 들어가기 때문 , 하지만 학습용 실습이나 특정 아키텍처에서는 외부와 소통하는 서비스(웹 서버, 로드밸런서, NAT 게이트웨이 등)가 위치하는 Public 영역을 넉넉하게 잡음</li>
</ul>
</li>
</ul>
<ul>
<li>주소 할당량은 정답이 있는 게 아니라, 해당 서브넷에 얼마나 많은 리소스를 배치할 것인가에 따른 설계자의 의도.. 실습에서는 Public 통신 테스트를 위해 좀 더 넉넉하게 할당한 것</li>
</ul>
<h4 id="step-3-public-서브넷-자동-할당-ip-설정"><strong>Step 3. Public 서브넷 자동 할당 IP 설정</strong></h4>
<ul>
<li>Public 서브넷 2개에 대해 &#39;퍼블릭 IPv4 주소 자동 할당&#39; 활성화</li>
<li>이 설정을 켜야만 이 서브넷에 생성되는 EC2들이 부팅될 때 자동으로 인터넷 주소(Public IP)를 부여받아 우리가 밖에서 접속할 수 있게 됌..</li>
</ul>
<h4 id="step-4-ec2-인스턴스-생성"><strong>Step 4. EC2 인스턴스 생성</strong></h4>
<ul>
<li>AMI: ubuntu</li>
<li>인스턴스 유형: t2.micro</li>
<li>키페어: 생성 또는 있는 거 사용</li>
<li>Public EC2: 보안그룹(SSH, HTTP), 퍼블릭 IP 활성화 (외부 접속용)</li>
<li>Private EC2: 보안그룹(SSH, HTTP), 퍼블릭 IP 비활성화 (내부망 전용)</li>
</ul>
<h4 id="step-5-인터넷-게이트웨이igw-생성-및-연결"><strong>Step 5. 인터넷 게이트웨이(IGW) 생성 및 연결</strong></h4>
<ul>
<li>IGW 생성 후 내가 만든 VPC에 연결</li>
<li>VPC라는 닫힌 성벽에 바깥세상으로 나가는 문을 하나 다는 것과 같음. 이 문이 없으면 아무리 Public IP가 있어도 인터넷 통신이 불가능.</li>
</ul>
<h4 id="step-6-라우팅-테이블route-table-설정"><strong>Step 6. 라우팅 테이블(Route Table) 설정</strong></h4>
<ul>
<li><strong>작업:</strong> Public용 라우팅 테이블 생성 → Public 서브넷 삽입 → 인터넷 게이트웨이 추가</li>
</ul>
<ul>
<li><p><strong>❓라우팅 테이블에 Public 서브넷을 왜 연결?</strong></p>
<ul>
<li>라우팅 테이블은 이정표임. 서브넷을 이 테이블에 연결하는 이유는 이 서브넷 안에 있는 모든 리소스는 이 이정표를 보고 길을 찾으라고 명령하는것.</li>
<li>연결하지 않으면 서브넷은 기본(Default) 라우팅 테이블을 따르는데, 그러면 외부로 나가는 길을 모를수도 있음. 따라서 너희(Public Subnet)는 인터넷 게이트웨이로 가는 길이 적힌 이 전용 이정표를 봐!라고 명시해 주는 것</li>
</ul>
</li>
<li><p><strong>❓인터넷 게이트웨이 왜 추가</strong></p>
<ul>
<li>이정표에 바깥세상(<code>0.0.0.0/0</code>)으로 가고 싶으면 아까 만든 그 성문(IGW)으로 가라는 내용을 적어 넣는 작업. 이 규칙이 있어야 비로소 진짜 퍼블릭 서브넷이 됌.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[ACC_핸즈온세션1]]></title>
            <link>https://velog.io/@cse23_ewha/ACC%ED%95%B8%EC%A6%88%EC%98%A8%EC%84%B8%EC%85%981</link>
            <guid>https://velog.io/@cse23_ewha/ACC%ED%95%B8%EC%A6%88%EC%98%A8%EC%84%B8%EC%85%981</guid>
            <pubDate>Tue, 31 Mar 2026 10:14:17 GMT</pubDate>
            <description><![CDATA[<h2 id="ip-및-dns-기초">IP 및 DNS 기초</h2>
<h4 id="ip-internet-protocol">IP (Internet Protocol)</h4>
<p>인터넷에 연결된 장치를 식별하는 고유 주소</p>
<ul>
<li>공인 IP: 인터넷에서 사용하는 주소</li>
<li>사설 IP: 내부 네트워크에서 사용하는 주소</li>
<li>고정 IP: 변하지 않는 주소</li>
<li>유동 IP: 바뀔 수 있는 주소</li>
</ul>
<h4 id="dns-domain-name-system">DNS (Domain Name System)</h4>
<p>도메인 이름과 IP 주소를 서로 변환해주는 분산형 데이터베이스</p>
<ul>
<li>도메인 → IP 변환: Forward DNS</li>
<li>IP → 도메인 변환: Reverse DNS</li>
<li>계층 구조: Root DNS → TLD DNS → Authoritative DNS 순으로 질의</li>
</ul>
<p>DNS는 하나의 서버가 모든 정보를 가지는 구조가 아니라, 각 서버가 일부 정보를 나누어 가지는 분산 구조</p>
<ul>
<li><strong>DNS 레코드 타입:</strong><ul>
<li><strong>A:</strong> 도메인을 IPv4 주소에 매핑.</li>
<li><strong>CNAME:</strong> 도메인을 다른 도메인에 매핑.</li>
<li><strong>NS:</strong> 도메인 관리 권한이 있는 네임 서버 지정.</li>
<li><strong>Alias:</strong> Route 53 전용, 도메인을 AWS 리소스에 직접 연결.</li>
</ul>
</li>
</ul>
<h3 id="네트워크-통신-흐름">네트워크 통신 흐름</h3>
<ul>
<li>DHCP(Dynamic Host Configuration Protocol) - 내 컴퓨터에 IP 주소를 자동으로 할당해주는 과정</li>
<li>ARP(Address Resolution Protocol) - IP 주소를 물리적 주소(MAC 주소)로 변환하는 과정</li>
<li>DNS</li>
<li>TCP(Transmission Control Protocol) - 데이터를 안전하게 주고받기 위해 연결하는 과정</li>
<li>HTTP/HTTPS </li>
</ul>
<p>전화기를 설치하고(DHCP), 상대방의 주소를 알아내고(ARP/DNS), 연결 라인을 확보한 뒤(TCP), 대화를 나누는(HTTP) 과정,,</p>
<h2 id="route-53">Route 53</h2>
<p><em>AWS에서 제공하는 DNS 서비스</em></p>
<p>단순히 도메인과 IP를 연결하는 것만이 아니라, 트래픽을 어떤 대상으로 보낼지 결정하는 기능도 함께 제공</p>
<ul>
<li><strong>역할:</strong> DNS + 헬스체크 + 장애 조치(Failover) + 라우팅 정책(GSLB)</li>
<li>주요 라우팅 정책:<ul>
<li><strong>단순:</strong> 표준 DNS 기능.</li>
<li><strong>가중치 기반:</strong> 설정한 비율로 트래픽 분산</li>
<li><strong>지리적 위치/근접:</strong> 사용자 또는 리소스 위치 기반 라우팅</li>
<li><strong>지연 시간:</strong> 응답 속도가 가장 빠른 리전으로 연결</li>
<li><strong>장애 조치:</strong> 대상이 정상일 때만 트래픽 전송</li>
<li><strong>다중 응답:</strong> 무작위로 여러 레코드 응답</li>
</ul>
</li>
</ul>
<p>로드 밸런서와 비슷해 보일 수 있지만 완전히 같지는 않음</p>
<ul>
<li>로드 밸런서: 들어온 트래픽을 여러 서버에 분산</li>
<li>Route 53: DNS 단계에서 어느 대상으로 보낼지 결정 </li>
</ul>
<h2 id="dns-레코드">DNS 레코드</h2>
<p><em>DNS 서버가 요청에 어떻게 응답할지 정의한 정보</em></p>
<p>기본 형식 : <strong>RR(Name, Value, Type, TTL)</strong></p>
<h3 id="주요-레코드">주요 레코드</h3>
<ul>
<li><p><strong>A 레코드</strong>
도메인 이름을 IPv4 주소에 매핑</p>
</li>
<li><p><strong>CNAME</strong>
하나의 도메인 이름을 다른 도메인 이름에 매핑</p>
</li>
<li><p><strong>NS</strong>
해당 도메인을 관리하는 네임서버 정보</p>
</li>
<li><p><strong>Alias</strong>
Route 53 전용 특수 레코드
AWS 리소스와 도메인을 연결할 때 사용</p>
</li>
<li><p><strong>TTL</strong>: DNS 응답을 캐시에 얼마나 오래 저장할지 정하는 값</p>
</li>
<li><p>테스트 시: 짧게 설정 / 운영 시: 보통 300초 이상 권장 </p>
</li>
</ul>
<h2 id="route-53-라우팅-정책">Route 53 라우팅 정책</h2>
<ul>
<li>단순 라우팅 -&gt; 기본 DNS 응답 방식</li>
<li>가중치 기반 라우팅 -&gt; 트래픽 비율 다르게 가능 [ 예) A 서버 80% /  B 서버 20% ]</li>
<li>지리적 위치 기반 라우팅 -&gt; 사용자 위치 기준으로 다른 대상으로 연결</li>
<li>지리적 근접 라우팅 -&gt; 리소스 위치 기준으로 가까운 곳에 연결</li>
<li>지연 시간 기반 라우팅 -&gt; 지연 시간이 가장 낮은 리전으로 연결</li>
<li>장애 조치 라우팅 -&gt; 정상인 대상에만 연결 / 헬스 체크와 함께 사용</li>
<li>다중 응답 라우팅 -&gt; 여러 레코드 중 일부를 무작위로 응답 </li>
</ul>
<h2 id="호스팅-영역">호스팅 영역</h2>
<p>DNS 레코드들을 저장해 두는 공간 -&gt; 특정 도메인에 대해 어떤 레코드를 둘지 관리하는 장소</p>
<ul>
<li>Public Hosted Zone: 인터넷에서 접근 가능</li>
<li>Private Hosted Zone: 같은 VPC 내부에서만 접근 가능 </li>
</ul>
<h2 id="vpc-virtual-private-cloud">VPC (Virtual Private Cloud)</h2>
<p>AWS 안에서 만드는 가상 네트워크
하나의 독립된 네트워크처럼 동작해서 보안, 관리, 확장 측면에서 유리</p>
<ul>
<li>리전 단위로 생성</li>
<li>리전당 기본 5개까지 생성 가능</li>
<li>VPC당 최대 5개의 CIDR 블록 설정 가능</li>
<li>사설 IPv4 범위만 할당 가능</li>
<li>다른 VPC나 외부 네트워크와 CIDR 대역이 겹치면 안 됨 </li>
</ul>
<h2 id="subnet">Subnet</h2>
<p>VPC를 더 작은 네트워크 단위로 나눈 것
VPC 안에서 리소스를 배치하는 IP 범위</p>
<ul>
<li>Public Subnet / Private Subnet</li>
</ul>
<p>특징</p>
<ul>
<li>서브넷은 하나의 AZ에만 속할 수 있음</li>
<li>서브넷의 IP 범위는 반드시 VPC의 CIDR 범위 안에 있어야 함</li>
</ul>
<h4 id="서브넷을-나누는-이유">서브넷을 나누는 이유</h4>
<ul>
<li>보안 격리</li>
<li>IP 주소 효율적 사용</li>
<li>관리 편의성 </li>
</ul>
<h2 id="cidr">CIDR</h2>
<p>IP 주소 범위를 표현하는 표기법</p>
<p>예시 ; <code>192.168.0.0/24</code></p>
<p>의미</p>
<ul>
<li>앞부분: 네트워크 ID (192.168.0.0)</li>
<li>뒷부분: 호스트 ID 범위 - 나머지 8비트(32비트 - 24비트)</li>
<li>서브넷 마스크 (/24): 앞의 24비트를 고정</li>
</ul>
<p>슬래시 뒤의 서브넷 마스크가 클수록 네트워크 ID 부분이 길어지고, 내가 쓸 수 있는 IP(호스트 ID 부분)는 적어짐</p>
<p>IP 주소 = <strong>네트워크 ID + 호스트 ID</strong> </p>
<p>AWS에서도 VPC와 서브넷의 주소 범위를 정할 때 CIDR 사용 </p>
<h2 id="igw-internet-gateway">IGW (Internet Gateway)</h2>
<p>VPC가 인터넷과 통신할 수 있도록 연결하는 게이트웨이</p>
<p>VPC는 기본적으로 외부와 격리된 네트워크라서 인터넷 연결을 위해 IGW가 필요
하지만 IGW만 붙인다고 바로 인터넷이 되는 것은 아님 -&gt; 인터넷 통신이 되려면 같이 필요</p>
<ul>
<li>VPC에 IGW 연결</li>
<li>라우팅 테이블에 인터넷 방향 경로 추가</li>
<li>공인 IP 또는 퍼블릭 접근 조건 충족 </li>
</ul>
<h2 id="route-table">Route Table</h2>
<p>트래픽을 어디로 보낼지 정하는 규칙표?</p>
<p>예를 들어 특정 목적지로 가는 요청을 로컬로 보낼지, IGW로 보낼지, NAT Gateway로 보낼지 결정</p>
<ul>
<li><code>local</code> → 같은 VPC 내부 통신</li>
<li><code>0.0.0.0/0 → IGW</code> → 인터넷 통신</li>
<li><code>0.0.0.0/0 → NAT Gateway</code> → 프라이빗 서브넷의 외부 통신</li>
</ul>
<p>하나의 라우팅 테이블을 여러 서브넷에 연결할 수도 있음 </p>
<h2 id="public-subnet--private-subnet">Public Subnet / Private Subnet</h2>
<h3 id="public-subnet">Public Subnet</h3>
<p>인터넷과 직접 연결 가능한 서브넷</p>
<ul>
<li>IGW 연결</li>
<li>라우팅 테이블에 인터넷 경로 존재</li>
<li>공인 IP 부여 가능</li>
<li>Bastion Host</li>
<li>NAT Gateway</li>
<li>Public EC2</li>
</ul>
<h3 id="private-subnet">Private Subnet</h3>
<p>인터넷에서 직접 접근 불가능한 서브넷</p>
<ul>
<li>DB</li>
<li>내부 서버</li>
<li>외부에 노출되면 안 되는 애플리케이션 서버 </li>
</ul>
<h2 id="nat-gateway">NAT Gateway</h2>
<p>Private Subnet의 리소스가 외부 인터넷으로 나갈 수 있게 해주는 장치</p>
<p><strong>내부 → 외부 가능</strong>
<strong>외부 → 내부 직접 접근 불가</strong></p>
<p>즉, 프라이빗 서브넷 안의 인스턴스가 패키지 다운로드, 외부 API 호출, 
업데이트 같은 작업은 가능하게 하고, 외부에서 직접 들어오는 연결은 막는 구조</p>
<p>동작 흐름 : Private Subnet 리소스 → NAT Gateway → IGW → 인터넷</p>
<ul>
<li>Public Subnet에 생성</li>
<li>Elastic IP 사용</li>
<li>특정 AZ에 생성</li>
<li>여러 AZ에 각각 만들면 가용성 향상</li>
<li>시간당 비용 + 데이터 처리 비용 발생 </li>
</ul>
<h2 id="bastion-host">Bastion Host</h2>
<p>외부에서 Private Subnet 내부 서버로 안전하게 접속하기 위한 중간 서버</p>
<p>보통 Public Subnet에 두고, 운영자는 먼저 Bastion Host에 접속한 뒤
그 내부에서 Private EC2로 SSH 접속</p>
<p>즉, 외부 사용자가 Private EC2에 바로 들어가는 것이 아니라
중간 관문을 하나 두는 방식</p>
<ul>
<li>보안 그룹 설정 예시
   Bastion Host: 특정 외부 IP만 SSH 허용
  Private EC2: Bastion Host에서 오는 SSH만 허용 </li>
</ul>
<h2 id="nacl">NACL</h2>
<p>Subnet 단위에서 트래픽을 제어하는 보안 장치</p>
<p>보안 그룹이 인스턴스 단위라면, NACL은 서브넷 단위?</p>
<ul>
<li>서브넷을 오가는 트래픽 제어</li>
<li>허용과 거부 규칙 모두 설정 가능</li>
<li>서브넷 생성 시 기본 NACL에 자동 연결</li>
<li>기본 NACL은 비교적 넓게 허용</li>
<li>사용자 정의 NACL은 처음에 허용 규칙 없이 시작</li>
<li>규칙 번호가 낮을수록 우선순위 높음 </li>
</ul>
<h2 id="보안-그룹-vs-nacl">보안 그룹 vs NACL</h2>
<table>
<thead>
<tr>
<th align="left">구분</th>
<th align="left">보안 그룹 (Security Group)</th>
<th align="left">네트워크 ACL (NACL)</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>적용 단위</strong></td>
<td align="left">인스턴스(EC2) 단위</td>
<td align="left">서브넷 단위</td>
</tr>
<tr>
<td align="left"><strong>상태 관리</strong></td>
<td align="left">Stateful (응답 트래픽 자동 허용)</td>
<td align="left">Stateless (응답 규칙 별도 필요)</td>
</tr>
<tr>
<td align="left"><strong>규칙</strong></td>
<td align="left">허용만 가능</td>
<td align="left">허용 및 거부 가능</td>
</tr>
<tr>
<td align="left"><strong>보안범위</strong></td>
<td align="left">EC2 인스턴스 하나에만 적용되는 보안</td>
<td align="left">Subnet 안에 있는 모든 EC2 인스턴스에 적용</td>
</tr>
<tr>
<td align="left"><strong>우선순위</strong></td>
<td align="left">모든 규칙 평가 후 허용/거부 여부 결정</td>
<td align="left">번호순 평가 (낮은 번호 우선) , 첫번째 비교로 허용/거부 여부 평가</td>
</tr>
</tbody></table>
<ul>
<li>더 세밀하게 인스턴스를 보호 → 보안 그룹</li>
<li>서브넷 단위로 큰 흐름 제어 → NACL </li>
</ul>
<hr>
<h2 id="aws-네트워크-구조">AWS 네트워크 구조</h2>
<ul>
<li>Region : AWS 데이터 센터가 모여 있는 지리적 영역</li>
<li>AZ (Availability Zone) : 하나 이상의 데이터 센터 묶음 / 여러 AZ가 모여 하나의 Region 구성</li>
<li>VPC : Region 안에 만드는 독립 네트워크</li>
<li>Subnet : VPC를 더 작게 나눈 네트워크 단위 ( 각 서브넷은 하나의 AZ에 속함 )</li>
</ul>
<h2 id="vpc-peering">VPC Peering</h2>
<p>서로 다른 VPC끼리 연결하는 방식</p>
<p>원래 VPC는 서로 독립적이지만, 피어링을 설정하면 마치 같은 네트워크처럼 통신 가능</p>
<ul>
<li>AWS 내부 네트워크로 연결</li>
<li>private IPv4 또는 IPv6 사용</li>
<li>서로 다른 계정 간 가능</li>
<li>서로 다른 리전 간 가능</li>
<li>VPC CIDR 대역이 겹치면 안 됨</li>
<li>피어링 관계는 전이되지 않음</li>
<li>라우팅 테이블도 함께 수정 필요 </li>
</ul>
<p>전이되지 않는다는 말 ;  A-B 연결, B-C 연결이 있어도 A와 C가 자동으로 연결X</p>
<h2 id="vpc-endpoint">VPC Endpoint</h2>
<p>VPC 내부에서 AWS 서비스로 직접 연결하는 방식</p>
<p>예를 들어 Private Subnet의 인스턴스가 S3, SNS 같은 AWS 서비스에 접근해야 할 때, 
굳이 NAT Gateway → IGW → 인터넷 경로를 거치지 않도록 해줌</p>
<ul>
<li>AWS 내부 네트워크 이용</li>
<li>더 안전</li>
<li>더 효율적</li>
<li>인터넷 우회 불필요</li>
</ul>
<h2 id="nat-gateway와-vpc-endpoint-차이">NAT Gateway와 VPC Endpoint 차이</h2>
<h4 id="nat-gateway-1">NAT Gateway</h4>
<p>인터넷을 통해 외부로 나가는 통로
경로 : Private Subnet → NAT Gateway → IGW → 외부 인터넷/AWS 서비스</p>
<h4 id="vpc-endpoint-1">VPC Endpoint</h4>
<p>AWS 내부망으로 바로 연결
경로 : Private Subnet → VPC Endpoint → AWS 서비스
AWS 서비스 접근만 필요하다면 VPC Endpoint가 더 적절한 경우가 많음 
장점: 보안성 향상, 속도개선, 비용 최적화</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[대상그룹 헬스체크 502 해결과정]]></title>
            <link>https://velog.io/@cse23_ewha/%EB%8C%80%EC%83%81%EA%B7%B8%EB%A3%B9-%ED%97%AC%EC%8A%A4%EC%B2%B4%ED%81%AC-502-%ED%95%B4%EA%B2%B0%EA%B3%BC%EC%A0%95</link>
            <guid>https://velog.io/@cse23_ewha/%EB%8C%80%EC%83%81%EA%B7%B8%EB%A3%B9-%ED%97%AC%EC%8A%A4%EC%B2%B4%ED%81%AC-502-%ED%95%B4%EA%B2%B0%EA%B3%BC%EC%A0%95</guid>
            <pubDate>Sat, 14 Mar 2026 11:51:37 GMT</pubDate>
            <description><![CDATA[<h2 id="aws-alb-502-에러-원인-애플리케이션-문제인-줄-알았는데-대상-그룹-protocol-version이-http2">AWS ALB 502 에러 원인: 애플리케이션 문제인 줄 알았는데, 대상 그룹 Protocol Version이 HTTP2..</h2>
<p>Spring Boot 애플리케이션을 AWS EC2에 배포하고, ALB(Application Load Balancer) 뒤에 연결해 두었는데 계속 <strong>502 Bad Gateway</strong> 가 발생했다. 처음에는 애플리케이션 자체 문제, 보안 그룹, 헬스체크 경로 문제를 의심했다. 그런데 결론적으로는 <strong>대상 그룹(Target Group)의 Protocol Version이 HTTP2로 설정되어 있었던 것</strong>이 핵심 원인이었다.</p>
<p>정리하면, 내 서비스는 일반적인 Spring Boot 백엔드였고 <code>8080</code> 포트에서 HTTP로 정상 동작하고 있었다. 직접 EC2 내부에서 확인해 보면 <code>/health</code> 엔드포인트는 <code>200 OK</code>를 반환했다. 그런데도 ALB 대상 그룹에서는 계속 <code>Unhealthy</code>가 떴고, 결국 502가 발생했다.</p>
<hr>
<h4 id="먼저-확인했던-것들">먼저 확인했던 것들</h4>
<p>처음에는 가장 흔한 원인부터 점검했다.</p>
<ul>
<li>애플리케이션이 실제로 <code>8080</code> 포트에서 실행 중인지</li>
<li><code>/health</code> 엔드포인트가 <code>200</code>을 반환하는지</li>
<li>대상 그룹 상태 검사 경로가 <code>/health</code>로 되어 있는지</li>
<li>성공 코드가 <code>200</code>인지</li>
<li>보안 그룹에서 포트 <code>8080</code>이 열려 있는지</li>
</ul>
<p>직접 EC2에서 확인했을 때는 <code>/health</code>가 정상 응답을 주고 있었다. 그래서 애플리케이션이 죽은 것은 아니었고, 헬스체크 경로도 틀리지 않았다.</p>
<p>또 AWS 문서상 ALB 대상 그룹의 상태 검사에서는 HTTP/1.1 또는 HTTP/2를 사용하는 경우 유효한 URI 경로를 지정할 수 있고, 성공 코드는 기본적으로 <code>200</code>이다. 따라서 <code>/health</code>, <code>200</code> 조합 자체는 문제 없는 설정이었다.</p>
<hr>
<h4 id="이상했던-점">이상했던 점</h4>
<p>문제는 대상 그룹 상세 설정을 자세히 보다가 발견했다.</p>
<ul>
<li>Protocol: <code>HTTP</code></li>
<li>Port: <code>8080</code></li>
<li><strong>Protocol version: <code>HTTP2</code></strong></li>
<li>Health check path: <code>/health</code></li>
<li>Success code: <code>200</code></li>
</ul>
<p>겉보기에는 크게 이상해 보이지 않을 수 있다. 하지만 AWS 공식 문서에 따르면, <strong>Application Load Balancer는 기본적으로 타깃에 HTTP/1.1로 요청을 보낸다.</strong> 다만 대상 그룹의 <strong>Protocol version</strong> 설정을 통해 타깃으로 <strong>HTTP/2 또는 gRPC</strong>를 사용하도록 바꿀 수 있다. 
즉, 나는 평범한 Spring Boot 백엔드를 올려 두었는데, 대상 그룹이 백엔드와 통신할 때 <strong>HTTP2를 쓰도록 설정되어 있었던 것</strong>이다.</p>
<hr>
<h4 id="왜-이게-문제가-되었나">왜 이게 문제가 되었나</h4>
<p>내 애플리케이션은 브라우저나 <code>curl</code>로 접근했을 때는 잘 응답했다. 이 테스트들은 사실상 일반적인 HTTP 요청 확인에 해당한다. 그런데 ALB는 대상 그룹 설정에 따라 타깃과 통신하는 방식이 달라질 수 있다.</p>
<p>AWS 문서에는 대상 그룹의 프로토콜 버전에 따라 요청 프로토콜과의 조합 결과가 달라진다고 명시되어 있다. 특히 <strong>HTTP/1.1 요청을 HTTP/2 대상 그룹으로 보내는 조합은 오류</strong>가 될 수 있다. 또한 프로토콜 버전이 맞지 않으면 ALB에서 프로토콜 불일치 문제가 발생할 수 있다고 설명한다. ([AWS Docs][2])</p>
<p>결국 내 경우는 다음과 같이 이해할 수 있었다.</p>
<ul>
<li>애플리케이션 자체는 정상</li>
<li><code>/health</code>도 정상 응답</li>
<li>하지만 ALB 대상 그룹이 백엔드와 통신할 때 HTTP2를 기대</li>
<li>실제 백엔드는 그 환경에 맞지 않음</li>
<li>상태 검사 실패</li>
<li>타깃이 <code>Unhealthy</code></li>
<li>최종적으로 502 발생</li>
</ul>
<hr>
<h4 id="해결-방법">해결 방법</h4>
<p>해결은 단순했다. 대상 그룹의 Protocol Version을 다음과 같이 바꾸면 되었다.</p>
<ul>
<li>Protocol: <code>HTTP</code></li>
<li>Port: <code>8080</code></li>
<li><strong>Protocol version: <code>HTTP1</code></strong></li>
<li>Health check path: <code>/health</code></li>
<li>Success code: <code>200</code></li>
</ul>
<p>AWS 공식 문서상 ALB는 기본적으로 타깃에 HTTP/1.1로 요청을 보내므로, 일반적인 Spring Boot 백엔드라면 이 설정이 가장 자연스럽다.
설정을 바꾼 뒤에는 대상 그룹 상태가 정상으로 바뀌고, 502도 해결되었다.
안되면 대상그룹 - 로드밸런서를 다시 만들면 HTTP1로 잘 적용된다!!!</p>
<hr>
<h2 id="왜-http2로-설정되어-있었는지는-아직-모르겠다">왜 HTTP2로 설정되어 있었는지는 아직 모르겠다</h2>
<p>솔직히 말하면, <strong>왜 대상 그룹 Protocol Version이 HTTP2로 바뀌어 있었는지는 나도 정확히 모르겠다.</strong>
내가 의도적으로 설정한 기억은 없다.</p>
<p>가능한 추측은 몇 가지 있다.</p>
<ul>
<li>대상 그룹 생성 시 콘솔에서 잘못 선택했을 가능성</li>
<li>기존 설정을 복사하거나 수정하는 과정에서 바뀌었을 가능성</li>
<li>다른 문서를 참고하면서 HTTP2가 더 “최신” 설정처럼 보여 무심코 선택했을 가능성</li>
</ul>
<p>하지만 중요한 것은 원인을 특정하는 것보다, <strong>백엔드 서버가 어떤 프로토콜을 기대하는지와 대상 그룹 설정이 일치해야 한다</strong>는 점이다.</p>
<hr>
<h2 id="이번-이슈에서-배운-점">이번 이슈에서 배운 점</h2>
<p>이번 문제를 겪으면서 느낀 것은, ALB 502가 난다고 해서 무조건 애플리케이션 코드 문제라고 보면 안 된다는 점이다.</p>
<p>내가 실제로 확인했던 순서는 이랬다.</p>
<ol>
<li>애플리케이션 프로세스/컨테이너가 살아 있는지 확인</li>
<li>EC2 내부에서 <code>/health</code>가 <code>200</code>인지 확인</li>
<li>대상 그룹의 헬스체크 경로와 성공 코드 확인</li>
<li>보안 그룹 확인</li>
<li>마지막으로 대상 그룹의 <strong>Protocol Version</strong> 확인</li>
</ol>
<p>특히 앞의 것들이 모두 정상인데도 타깃이 계속 <code>Unhealthy</code>라면, <strong>Protocol Version까지 반드시 확인해야 한다.</strong></p>
<hr>
<h2 id="한-줄-결론">한 줄 결론</h2>
<p><strong>Spring Boot 백엔드는 정상인데 ALB에서만 헬스체크가 실패하고 502가 난다면, 대상 그룹의 Protocol Version이 HTTP2로 되어 있지 않은지 꼭 확인하자. 일반적인 백엔드라면 HTTP1 설정이 맞을 가능성이 크다.</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[코코베이] 아이디어창업프로젝트 회고록]]></title>
            <link>https://velog.io/@cse23_ewha/%EC%BD%94%EC%BD%94%EB%B2%A0%EC%9D%B4-%EC%95%84%EC%9D%B4%EB%94%94%EC%96%B4%EC%B0%BD%EC%97%85%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0%EB%A1%9D</link>
            <guid>https://velog.io/@cse23_ewha/%EC%BD%94%EC%BD%94%EB%B2%A0%EC%9D%B4-%EC%95%84%EC%9D%B4%EB%94%94%EC%96%B4%EC%B0%BD%EC%97%85%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0%EB%A1%9D</guid>
            <pubDate>Wed, 11 Mar 2026 01:40:52 GMT</pubDate>
            <description><![CDATA[<h3 id="코코베이-아이디어창업프로젝트-회고록"><strong>[코코베이] 아이디어창업프로젝트 회고록</strong></h3>
<hr>
<h3 id="아이템-요약">아이템 요약</h3>
<p>육아 초보 부모들을 위한 개인 맞춤형 육아용품 큐레이션 앱 코코베이(Cocobay)를 구상</p>
<p>과잉 정보 속에서 제품을 고르기 어려운 부모들을 위해 월령별 가이드, AI 추천, 예산 계산기, 할인 알림 등 기능을 제공</p>
<h3 id="팀-구성-및-역할">팀 구성 및 역할</h3>
<ul>
<li>기획: 육아용품 시장 리서치, 사용자 인터뷰, 페르소나 설정</li>
<li>디자인: UI/UX</li>
<li>프론트엔드 개발: 추천 홈화면, 예산 계산기, 할인 알림 기능 구현</li>
<li>AI 모델 개발: AI 렌즈 기반 유사 상품 탐색 기능 초기 모델 구현</li>
</ul>
<h3 id="프로젝트-진행-과정">프로젝트 진행 과정</h3>
<ul>
<li>육아 부모 대상 설문 및 심층 인터뷰 진행</li>
<li>맘카페 크롤링을 통한 정보 탐색 피로 분석</li>
<li>기획 → 디자인 → 프론트 구현 → AI 모델 → 발표자료 제작까지 진행</li>
<li><strong>Notion + Figma + GitHub</strong>로 협업</li>
</ul>
<h3 id="잘했다고-생각하는-점">잘했다고 생각하는 점</h3>
<ul>
<li>사용자 중심 문제 정의가 명확했고, 인터뷰 기반 페르소나가 구체적이었음</li>
<li>실사용자의 고민을 직접 반영한 기능 기획</li>
<li>디자인과 개발 간 협업이 매끄럽고 속도감 있게 진행됨</li>
<li>실제 프론트엔드 구현물 + AI 모델까지 일정 내에 완성</li>
</ul>
<h3 id="아쉬운-점">아쉬운 점</h3>
<ul>
<li>데이터 기반 기능의 정확도와 검증 부분은 시간이 부족해 MVP 수준에 머물렀음</li>
<li>백엔드 연동하지 못하고 프론트에서 그친것이 아쉬움</li>
<li>체험단 확보 및 초기 홍보 전략을 실제 실행해보진 못하여 유저 피드백 수집 및 반복 개선을 하지 못한 점이 아쉬움</li>
</ul>
<h3 id="향후-발전-방향">향후 발전 방향</h3>
<ul>
<li>AI 렌즈 정확도 개선 및 자체 데이터셋 구축</li>
<li>제휴몰 연동을 통한 수익화 실험</li>
</ul>
<h3 id="개인적으로-얻은-것">개인적으로 얻은 것</h3>
<ul>
<li>사용자 인터뷰부터 실 프로토타입 개발까지, 스타트업의 문제 정의 – 솔루션 설계 – MVP 개발 – 피칭 전 과정을 경험</li>
<li>팀 내 빠른 의사결정과 다른 파트 팀원들과의 협업의 중요성을 체감</li>
<li>피봇되는게 당연하고 이 과정을 단순히 힘들다고 회피하지 않기</li>
<li>아이디어만 있다면 충분히 공부해서라도 구체화 가능하다. 너무 불가능하다고 생각하지 말기!</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[클라우드컴퓨팅]]></title>
            <link>https://velog.io/@cse23_ewha/%ED%81%B4%EB%9D%BC%EC%9A%B0%EB%93%9C%EC%BB%B4%ED%93%A8%ED%8C%85</link>
            <guid>https://velog.io/@cse23_ewha/%ED%81%B4%EB%9D%BC%EC%9A%B0%EB%93%9C%EC%BB%B4%ED%93%A8%ED%8C%85</guid>
            <pubDate>Mon, 02 Mar 2026 06:38:56 GMT</pubDate>
            <description><![CDATA[<h2 id="ch2">ch2</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/094a8a0d-b03f-4869-a08b-eb7e1eb53b32/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/cse23_ewha/post/d6a24342-8d3c-49de-95ff-ba004b53d698/image.jpg" alt=""></p>
<h2 id="ch3">ch3</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/6bdcb85a-0ee9-433f-bd00-106d5d4b984c/image.jpg" alt=""></p>
<h2 id="ch4">ch4</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/4da410b7-39ab-4eb3-9069-3bacdcc7f2f9/image.jpg" alt=""></p>
<h2 id="ch5">ch5</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/d8220b4b-df65-4152-a8b4-a51c1d9e26ca/image.jpg" alt=""></p>
<h2 id="ch6">ch6</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/f1380915-f669-41e0-bb6c-4d7f1e6c881c/image.jpg" alt=""></p>
<h2 id="ch7">ch7</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/b493091e-d3a7-4986-905d-aba9f7c3a6d7/image.jpg" alt=""></p>
<h2 id="ch8">ch8</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/223c247c-72e6-4fb6-bd57-84331a256d96/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/cse23_ewha/post/6b9c4bdf-0b94-4861-a076-637958e52297/image.jpg" alt=""></p>
<h2 id="ch9">ch9</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/ba7511ba-96d1-46a7-b9cb-0c75c52f2657/image.jpg" alt=""></p>
<h2 id="ch10">ch10</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/c9be376e-9801-4db4-b858-034fdbba6dd9/image.jpg" alt=""></p>
<h2 id="ch11">ch11</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/922f041f-99f1-40e1-be25-1470a8dd0d2a/image.jpg" alt=""></p>
<h2 id="ch12">ch12</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/21344081-60f2-4ef9-9a9f-cbe94cf2417f/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/cse23_ewha/post/44d306c7-f2a0-47de-9813-83b582b31248/image.jpg" alt=""></p>
<h2 id="ch13">ch13</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/738ae1cc-4013-4ffc-a212-10abe2c8361d/image.jpg" alt=""></p>
<h2 id="ch14">ch14</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/47aa0d67-f0cb-4a8c-8a66-7defbd8de4a2/image.jpg" alt=""></p>
<h2 id="ch15">ch15</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/59c589fe-9bae-4a87-950b-526bc588a62f/image.jpg" alt=""></p>
<h2 id="ch16">ch16</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/135224f0-7055-4e6e-bbc6-160ce169a9d3/image.jpg" alt=""></p>
<h2 id="ch17">ch17</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/9e515b08-3a39-4513-bf7c-1df4cf54005b/image.jpg" alt=""><img src="https://velog.velcdn.com/images/cse23_ewha/post/4e8cd1f5-16da-4274-9379-64ff8b129b32/image.jpg" alt=""></p>
<h2 id="ch18">ch18</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/96b4e123-ad33-439b-83c5-c0fec7e3d6ab/image.jpg" alt=""><img src="https://velog.velcdn.com/images/cse23_ewha/post/7b50afad-7519-43e0-b386-ed44a03abfe8/image.jpg" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[소프트웨어공학]]></title>
            <link>https://velog.io/@cse23_ewha/%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4%EA%B3%B5%ED%95%99</link>
            <guid>https://velog.io/@cse23_ewha/%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4%EA%B3%B5%ED%95%99</guid>
            <pubDate>Mon, 02 Mar 2026 06:03:16 GMT</pubDate>
            <description><![CDATA[<h2 id="개요">개요</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/b52734e2-9b2c-4735-a979-de9ef43290d8/image.jpg" alt=""></p>
<h2 id="소프트웨어-프로세스-1">소프트웨어 프로세스 1</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/48332305-beb6-4783-bafd-20172bdf9b88/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/cse23_ewha/post/fca8166d-cee3-4a87-8785-98592af81c15/image.jpg" alt=""></p>
<h2 id="소프트웨어-프로세스-2">소프트웨어 프로세스 2</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/76cc8280-8156-4cf0-8688-07d17da62f75/image.jpg" alt=""></p>
<h2 id="sw-모델링-및-표현도구">sw 모델링 및 표현도구</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/9e8bc194-4cbf-421e-b5c5-e11fc034c941/image.jpg" alt=""></p>
<h2 id="요구공학">요구공학</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/2015a765-01bc-41fb-80d5-c0c2f6cf70e5/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/cse23_ewha/post/75b72dbc-2f92-4ca6-a45e-38679415f9ce/image.jpg" alt=""></p>
<h2 id="시스템-모델링">시스템 모델링</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/dfa4e4d1-891f-4d7c-aceb-f95aaa6a9e01/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/cse23_ewha/post/80a68664-50fd-49dd-bae5-ebc3356ead06/image.jpg" alt=""></p>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/57b916a6-9e6d-4c66-8f06-0a22fdb488bc/image.jpg" alt=""></p>
<h2 id="아키텍쳐설계">아키텍쳐설계</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/65b84ca3-1a8a-4f94-ab30-2282914179f8/image.jpg" alt=""></p>
<h2 id="객체지향소프트웨어">객체지향소프트웨어</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/d7512f4b-66d9-4fc8-964f-bf59580b5225/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/cse23_ewha/post/b93e69b0-b105-4886-a8f4-d4db8bf7a830/image.jpg" alt=""></p>
<h2 id="ch8">ch8</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/746b3e6c-7821-417b-aa9e-80ad5afc814b/image.jpg" alt=""></p>
<h2 id="ch9">ch9</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/1720e28c-f07a-4aac-8882-16ae1073957d/image.jpg" alt=""></p>
<h2 id="ch10">ch10</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/56d7407e-a357-426d-ae7d-c304251d28fe/image.jpg" alt=""></p>
<h2 id="ch11">ch11</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/0b75f9b5-8a67-44ab-9c78-fd72cbe31bfe/image.jpg" alt=""></p>
<h2 id="ch12">ch12</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/1967f1c5-851a-4766-b62e-b93092baa01f/image.jpg" alt=""></p>
<h2 id="ch13">ch13</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/d7bdbe87-318e-4b56-8c7f-d76b198e1aa9/image.jpg" alt=""></p>
<h2 id="ch14">ch14</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/585ef8d1-d77d-49db-8d35-1ff9820d0897/image.jpg" alt=""></p>
<h2 id="ch15">ch15</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/789a530b-d9e8-4968-bcec-7f69c77accf6/image.jpg" alt=""></p>
<h2 id="ch16">ch16</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/c40a0d34-3591-43a2-89bf-2b95cdc0467a/image.jpg" alt=""></p>
<h2 id="ch17">ch17</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/c99b72f4-f471-48e4-93d5-eb1c42f6c667/image.jpg" alt=""></p>
<h2 id="ch18">ch18</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/eb862c6e-1e58-49f9-a7a0-a8eaceebdc5d/image.jpg" alt=""></p>
<h2 id="ch19">ch19</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/ae39ee1d-df66-4bbf-ba94-13457c3dd736/image.jpg" alt=""></p>
<h2 id="ch20">ch20</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/02a4d827-8c8d-47db-91aa-cda634d7b6f5/image.jpg" alt=""></p>
<h2 id="ch21">ch21</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/e88abc7f-d9c0-4f4a-9d59-551418f86eb3/image.jpg" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[빅 데이터 응용]]></title>
            <link>https://velog.io/@cse23_ewha/%EB%B9%85-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%9D%91%EC%9A%A9-hvla3lyl</link>
            <guid>https://velog.io/@cse23_ewha/%EB%B9%85-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%9D%91%EC%9A%A9-hvla3lyl</guid>
            <pubDate>Mon, 02 Mar 2026 05:41:31 GMT</pubDate>
            <description><![CDATA[<p>ch1
<img src="https://velog.velcdn.com/images/cse23_ewha/post/d456578a-b76d-4d16-8b3c-d4cf31b4783c/image.jpg" alt=""></p>
<p>ch2
<img src="https://velog.velcdn.com/images/cse23_ewha/post/6327f22f-1475-4f07-8216-b61a390e4647/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/cse23_ewha/post/beed9222-909b-4870-a5eb-e3d9ea25c7aa/image.jpg" alt=""></p>
<p>ch3</p>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/a2c04615-77f7-4e26-8911-29ab2398f951/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/cse23_ewha/post/d2ceff28-b65e-4a11-88aa-4fb72bf34434/image.jpg" alt=""></p>
<p>ch4
<img src="https://velog.velcdn.com/images/cse23_ewha/post/2ba67c3b-f061-4f34-b975-1d8bcfe7d68a/image.jpg" alt="">
ch5
<img src="https://velog.velcdn.com/images/cse23_ewha/post/8f6301c8-0961-4a6b-8b7f-2169b56e5b8c/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/cse23_ewha/post/3182c5eb-1f17-4c4d-a70a-e8f6150c144d/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/cse23_ewha/post/b9375d67-a5e0-4535-adaa-30560354b28e/image.jpg" alt=""></p>
<p>ch6
<img src="https://velog.velcdn.com/images/cse23_ewha/post/7128fa0d-5a43-42c3-9e2a-55723e6ad6eb/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/cse23_ewha/post/0e675293-8a2f-42bd-af22-194abea6e1bd/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/cse23_ewha/post/946b4472-36ec-4c0f-b3fc-760a0dec07c7/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/cse23_ewha/post/4dd51b8c-1b82-4cd5-8fa7-1d7d89936710/image.jpg" alt=""></p>
<p>ch7
<img src="https://velog.velcdn.com/images/cse23_ewha/post/3f57a091-44f8-4e37-a379-9b0ae36d500e/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/cse23_ewha/post/0ddbc689-cdf9-4166-a9ee-e2a0889fa5ae/image.jpg" alt="">
ch8
<img src="https://velog.velcdn.com/images/cse23_ewha/post/a338b011-3b3a-4a9e-8a39-d39382cdb06e/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/cse23_ewha/post/370914a1-06e6-4a2c-a56b-5395f6bf151a/image.jpg" alt="">
ch9
<img src="https://velog.velcdn.com/images/cse23_ewha/post/e7fe060f-944b-44f3-b57d-735e929d6b2b/image.jpg" alt=""></p>
<h2 id="족보-풀이">족보 풀이</h2>
<p><img src="https://velog.velcdn.com/images/cse23_ewha/post/db2693a2-dcdc-4a98-a7b5-f0a5c6f8313a/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/cse23_ewha/post/de08e405-7483-4047-9f06-6a236a3e3ae4/image.jpg" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[FastAPI 기초 문법]]></title>
            <link>https://velog.io/@cse23_ewha/FastAPI-%EA%B8%B0%EC%B4%88-%EB%AC%B8%EB%B2%95</link>
            <guid>https://velog.io/@cse23_ewha/FastAPI-%EA%B8%B0%EC%B4%88-%EB%AC%B8%EB%B2%95</guid>
            <pubDate>Sat, 24 Jan 2026 03:57:47 GMT</pubDate>
            <description><![CDATA[<p>몰입캠프 3주차를 위한 FastAPI 문법 속성으로 공부하기</p>
<hr>
<h3 id="1-기본-골격">1. 기본 골격</h3>
<p>가장 기초적인 형태
<code>@app.get(&quot;/&quot;)</code> 같은 데코레이터가 이 주소로 오면 아래 함수를 실행해라고 알려줌</p>
<pre><code class="language-python">from fastapi import FastAPI

app = FastAPI()

# GET 요청을 받음
@app.get(&quot;/&quot;)
async def read_root():
    return {&quot;message&quot;: &quot;Hello World&quot;}
</code></pre>
<ul>
<li><strong><code>async def</code></strong>: 비동기 함수로 정의 <ul>
<li>데이터베이스나 AI 요청처럼 오래 걸리는 작업에 필수 -&gt; ai가 오래걸려도 그걸 기다지리않고 다른걸 할수있음</li>
</ul>
</li>
<li><strong>Return</strong>: 딕셔너리(<code>{}</code>)를 리턴하면 자동으로 JSON으로 변환되어 나감</li>
</ul>
<hr>
<h3 id="2-경로-변수-path-parameter">2. 경로 변수 (Path Parameter)</h3>
<p>URL 경로 자체에 들어있는 변수를 받음
예: <code>/items/5</code> -&gt; <code>item_id</code>는 5</p>
<pre><code class="language-python">@app.get(&quot;/items/{item_id}&quot;)
async def read_item(item_id: int):  # 타입 힌트(int) 필수!
    return {&quot;item_id&quot;: item_id}
</code></pre>
<ul>
<li><strong><code>item_id: int</code></strong>: 이렇게 타입을 적어두면, 사용자가 <code>/items/foo</code>라고 문자를 넣었을 때 FastAPI가 알아서 숫자만 넣으라고 에러를 냄</li>
</ul>
<hr>
<h3 id="3-쿼리-파라미터-query-parameter">3. 쿼리 파라미터 (Query Parameter)</h3>
<p>URL 뒤에 <code>?</code>로 붙는 변수
경로(<code>{}</code>)에 없는 변수를 함수 인자로 넣으면 자동으로 쿼리 파라미터가 됌
예: <code>/items/?skip=0&amp;limit=10</code></p>
<pre><code class="language-python">@app.get(&quot;/items/&quot;)
async def read_items(skip: int = 0, limit: int = 10):
    return {&quot;skip&quot;: skip, &quot;limit&quot;: limit}
</code></pre>
<ul>
<li><strong><code>skip: int = 0</code></strong>: 값이 안 들어오면 기본값 0 사용</li>
</ul>
<hr>
<h3 id="4-데이터-받기">4. 데이터 받기</h3>
<p>POST 요청으로 복잡한 데이터를 받을 때 사용
<code>Pydantic</code>이라는 라이브러리로 데이터의 모양(스키마)을 정의</p>
<pre><code class="language-python">from pydantic import BaseModel

# 1. 데이터 모양 정의 (붕어빵 틀)
class Item(BaseModel):
    name: str
    price: float
    is_offer: bool = None  # 선택 사항

# 2. 함수에서 사용
@app.post(&quot;/items/&quot;)
async def create_item(item: Item): # item 변수는 Item 클래스 모양이어야 함
    return {&quot;name&quot;: item.name, &quot;price&quot;: item.price}
</code></pre>
<ul>
<li>사용자가 JSON 데이터를 보내면, FastAPI가 <code>Item</code> 클래스 모양과 맞는지 검사하고, 자동으로 변수(<code>item</code>)에 넣어줌</li>
<li><code>item.name</code>처럼 점(<code>.</code>)을 찍어서 데이터에 접근할 수 있음</li>
</ul>
<hr>
<h3 id="5-라우터-나누기-apirouter">5. 라우터 나누기 (APIRouter)</h3>
<p>기능별로 파일을 쪼갤 때 사용 </p>
<p><strong>파일: `users.py</strong>`</p>
<pre><code class="language-python">from fastapi import APIRouter

router = APIRouter()

@router.get(&quot;/users/&quot;)
async def read_users():
    return [{&quot;username&quot;: &quot;Rick&quot;}, {&quot;username&quot;: &quot;Morty&quot;}]
</code></pre>
<p><strong>파일: <code>main.py</code> (메인)</strong></p>
<pre><code class="language-python">from fastapi import FastAPI
from . import users # users.py 불러오기

app = FastAPI()

# users의 라우터를 메인에 합체!
app.include_router(users.router)
</code></pre>
<hr>
<h3 id="자동-문서화-swagger-ui">자동 문서화 (Swagger UI)</h3>
<p>FastAPI의 가장 큰 장점
코드를 짜고 서버를 켜면, 자동으로 API 설명서 사이트를 만들어줌</p>
<ul>
<li>서버 실행 후 주소창에: <code>http://localhost:8000/docs</code></li>
<li>API를 직접 테스트 가능 (Postman 없어도 됨)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[웹소켓(WebSocket) 성공 기록]]></title>
            <link>https://velog.io/@cse23_ewha/%EC%9B%B9%EC%86%8C%EC%BC%93WebSocket-%EC%84%B1%EA%B3%B5-%EA%B8%B0%EB%A1%9D</link>
            <guid>https://velog.io/@cse23_ewha/%EC%9B%B9%EC%86%8C%EC%BC%93WebSocket-%EC%84%B1%EA%B3%B5-%EA%B8%B0%EB%A1%9D</guid>
            <pubDate>Thu, 22 Jan 2026 04:22:14 GMT</pubDate>
            <description><![CDATA[<p>카이스트 몰입캠프 2주차 프로젝트 결과물인 몰 봐(MV) 서비스의 핵심인 <strong>&quot;댓글 기반 팟 모집 -&gt; 채팅방 생성 -&gt; 실시간 채팅&quot;</strong> 로직을 바탕으로 웹소켓(WebSocket) 구현 성공 기록기...</p>
<hr>
<h4 id="1-웹소켓과-stomp-기초-개념">1. 웹소켓과 STOMP 기초 개념</h4>
<p>단순 웹소켓만 쓰면 로우 레벨이라 메시지 형식을 일일이 다 정해야 함
그래서 우리는 <strong>STOMP</strong> 규격 사용했음</p>
<ul>
<li><p><strong>구조</strong>: 메일함처럼 특정 주소를 &#39;구독(Sub)&#39;하고, 그 주소로 메시지를 &#39;발행(Pub)&#39;하는 방식임.</p>
</li>
<li><p><strong>장점</strong>: 메시지 브로커를 통해 채팅방별로 메시지를 꽂아주기가 훨씬 편함.</p>
</li>
<li><p><strong>Publisher(발행자)</strong>: 채팅 메시지를 보내는 사람 (클라이언트)</p>
</li>
<li><p><strong>Subscriber(구독자)</strong>: 채팅방에 들어와서 메시지를 받는 사람 (클라이언트)</p>
</li>
<li><p><strong>Broker(중개자)</strong>: 메시지를 받아서 구독 중인 사람들에게 뿌려주는 서버</p>
</li>
</ul>
<hr>
<h4 id="2-백엔드-설정-spring-boot">2. 백엔드 설정 (Spring Boot)</h4>
<p>먼저 의존성에 <code>spring-boot-starter-websocket</code> 추가되어 있어야 함.</p>
<p><strong>[WebSocketConfig.java]</strong></p>
<pre><code class="language-java">@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        // 메시지 받을 때: /sub/chat/room/1 이런 식으로 구독함
        config.enableSimpleBroker(&quot;/sub&quot;); 
        // 메시지 보낼 때: /pub/chat/message 이런 식으로 보냄
        config.setApplicationDestinationPrefixes(&quot;/pub&quot;);
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint(&quot;/ws&quot;) // 프론트가 접속할 엔드포인트
                .setAllowedOrigins(&quot;https://www.example.com&quot;) // 프론트 도메인 필수
                .withSockJS(); // 낮은 버전 브라우저 대응
    }
}
</code></pre>
<hr>
<h4 id="3-채팅-로직-흐름-우리-서비스-특화">3. 채팅 로직 흐름 (우리 서비스 특화)</h4>
<p> <strong>글쓴이가 게시글(Post) 댓글에서 선택해서 팟 참여 확정 -&gt; 채팅방 생성</strong> 구조로 기획을 했음..</p>
<ol>
<li><strong>팟 확정</strong>: 게시글 작성자가 참여자를 선택하고 &#39;확정&#39; 누름.</li>
<li><strong>채팅방 생성</strong>: 서버에서 <code>ChatRoom</code> 만들고 선택된 인원들을 <code>ChatMember</code>로 등록함.</li>
<li><strong>알림 발송</strong>: 참여자들에게 &quot;채팅방에 초대되었습니다&quot;라고 알림 쏨.</li>
<li><strong>입장 및 구독</strong>: 프론트에서 <code>roomId</code>를 받아 해당 경로(<code>/sub/chat/room/{id}</code>)를 구독함.</li>
<li><strong>메시지 전송(Publish)</strong>: 유저가 채팅 치면 프론트에서 <code>/pub/chat/message</code>로 JSON 데이터를 전송</li>
</ol>
<p><strong>[ChatController.java]</strong></p>
<pre><code class="language-java">@Controller
@RequiredArgsConstructor
public class ChatController {
    private final SimpMessageSendingOperations messagingTemplate;

    @MessageMapping(&quot;/chat/message&quot;) // 프론트가 /pub/chat/message로 보낼 때
    public void message(ChatMessageDto message) {
        // DB에 채팅 내역 저장하는 로직 추가 가능
        // 구독 중인 사람들에게 메시지 전달
        messagingTemplate.convertAndSend(&quot;/sub/chat/room/&quot; + message.getRoomId(), message);
    }
}
</code></pre>
<hr>
<h4 id="4-프론트엔드와-협업할-때-주의사항">4. 프론트엔드와 협업할 때 주의사항</h4>
<p><strong>① 보안 및 인증 (가장 많이 막히는 부분)</strong></p>
<ul>
<li>웹소켓은 일반 HTTP 요청이랑 다르게 Header에 토큰 담기가 까다로움....</li>
<li><strong>해결</strong>: 연결 시점(<code>connect</code>)에 쿼리 파라미터로 토큰을 보내거나, STOMP의 <code>connectCallback</code> 헤더에 JWT 토큰을 실어 보내야 함. 백엔드에서 <code>ChannelInterceptor</code>를 구현해서 토큰 검증 로직 따로 짰음</li>
</ul>
<p><strong>② CORS 에러</strong></p>
<ul>
<li>웹소켓 엔드포인트(<code>.addEndpoint(&quot;/ws&quot;)</code>)에도 <code>.setAllowedOrigins</code> 설정을 정확히 해줘야 함. 안 그러면 프론트에서 접속 못 함......</li>
</ul>
<p><strong>③ 도메인 연결 (HTTPS/WSS)</strong></p>
<ul>
<li>서비스 HTTPS 적용했다면 프론트도 <code>ws://</code>가 아니라 <code>wss://</code>로 접속해야 함.</li>
<li>AWS ALB(로드밸런서) 쓰고 있으면 웹소켓 프로토콜도 지원하도록 설정 확인해야 함 (기본적으로 지원하지만 간혹 타임아웃 이슈 있음).</li>
</ul>
<hr>
<h4 id="5-오류-해결-및-팁">5. 오류 해결 및 팁</h4>
<ul>
<li><strong>채팅방 자동 생성 이슈</strong>: 댓글에서 선택해서 채팅방 만들 때, 선택된 인원들의 <code>Member</code> ID 리스트를 받아서 <code>ChatMember</code>(중간 테이블)에 저장해야 했음. 그래야 나중에 &quot;내 채팅방 목록&quot; 조회할 때 내가 속한 방만 가져올 수 있었음</li>
<li><strong>무한 루프 방지</strong>: 프론트에서 메시지 받고 바로 다시 메시지 쏘는 로직 들어가면 서버 터짐. 이벤트 리스너 관리 잘하라고 프론트한테 말해줘서 해야함!</li>
</ul>
<hr>
<h4 id="사소하지만-디테일-구현하고자-한-포인트">사소하지만 디테일 구현하고자 한 포인트</h4>
<p>유저마다 <code>last_read_at</code>(마지막 읽은 시간)을 필드로 둠. 채팅방 목록 불러올 때 이 시간 이후로 쌓인 메시지 개수를 쿼리로 날려서 &quot;안 읽은 메시지 N개&quot; 기능을 구현했음..</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[가비아 도메인으로 Full-Stack 서비스 배포 및 HTTPS 보안 적용하기]]></title>
            <link>https://velog.io/@cse23_ewha/%EA%B0%80%EB%B9%84%EC%95%84-%EB%8F%84%EB%A9%94%EC%9D%B8%EC%9C%BC%EB%A1%9C-Full-Stack-%EC%84%9C%EB%B9%84%EC%8A%A4-%EB%B0%B0%ED%8F%AC-%EB%B0%8F-HTTPS-%EB%B3%B4%EC%95%88-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@cse23_ewha/%EA%B0%80%EB%B9%84%EC%95%84-%EB%8F%84%EB%A9%94%EC%9D%B8%EC%9C%BC%EB%A1%9C-Full-Stack-%EC%84%9C%EB%B9%84%EC%8A%A4-%EB%B0%B0%ED%8F%AC-%EB%B0%8F-HTTPS-%EB%B3%B4%EC%95%88-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 22 Jan 2026 04:04:10 GMT</pubDate>
            <description><![CDATA[<p>협업 프로젝트를 하며 프론트와 협업하며 경험한 HTTPS하는법 정리본!
딱 한번 해보면 어렵지 않음.</p>
<hr>
<h3 id="full-stack-https-인프라-구축-완벽-정리">Full-Stack HTTPS 인프라 구축 완벽 정리</h3>
<h4 id="1-인프라-구조-설계">1. 인프라 구조 설계</h4>
<ul>
<li><strong>Front-end</strong>: Vercel (자동 HTTPS 적용됨) → <code>www.example.com</code></li>
<li><strong>Back-end</strong>: AWS EC2 + ALB (수동 설정 필요) → <code>api.example.com</code></li>
<li><strong>핵심</strong>: 하나의 도메인을 사서 서브도메인으로 앞뒤를 나눠서 관리함.</li>
</ul>
<h4 id="2-도메인-구매-및-route-53-연결">2. 도메인 구매 및 Route 53 연결</h4>
<ol>
<li><strong>도메인 구매</strong>: 가비아 등에서 맘에 드는 거 삼.</li>
<li><strong>호스팅 영역 생성</strong>: AWS Route 53 가서 구매한 도메인으로 &#39;퍼블릭 호스팅 영역&#39; 만듦.</li>
<li><strong>네임서버(NS) 교체</strong>:</li>
</ol>
<ul>
<li>Route 53에서 생성된 NS 레코드 값 4개 복사함.</li>
<li>가비아 관리 페이지 가서 기존 네임서버 지우고 복사한 값 넣음. (맨 끝 마침표 <code>.</code> 빼고)
<em>전파되는데 시간 좀 걸림</em></li>
</ul>
<h4 id="3-ssl-인증서-발급-acm">3. SSL 인증서 발급 (ACM)</h4>
<ol>
<li><strong>인증서 요청</strong>: AWS ACM 가서 <code>*.example.com</code> , <code>example.com</code>  형태로 2개 포함해 신청</li>
<li><strong>DNS 검증</strong>: 검증 방식은 &#39;DNS 검증&#39; 선택</li>
<li><strong>레코드 생성</strong>: &quot;Route 53에서 레코드 생성&quot; 버튼 누르면 알아서 CNAME 생성</li>
<li><strong>상태 확인</strong>: &#39;발급됨&#39; 뜰 때까지 대기 ( 시간소요)</li>
</ol>
<h4 id="4-로드밸런서alb-및-타겟-그룹-세팅">4. 로드밸런서(ALB) 및 타겟 그룹 세팅</h4>
<ol>
<li><strong>대상 그룹(Target Group) 생성</strong>:</li>
</ol>
<ul>
<li>유형은 &#39;인스턴스&#39;, 포트는 스프링 부트 포트(8080)로 설정</li>
<li>내 EC2 인스턴스 체크해서 타겟에 포함시키기</li>
</ul>
<ol start="2">
<li><strong>ALB 생성</strong>:</li>
</ol>
<ul>
<li>Application Load Balancer 선택</li>
<li>가용 영역(AZ)은 최소 2개 이상의 퍼블릭 서브넷 선택해야함 (나는 a,c선택함)</li>
<li><strong>보안 그룹</strong>: 80(HTTP), 443(HTTPS) 둘 다 열려 있어야 함.</li>
</ul>
<ol start="3">
<li><strong>리스너 설정</strong>:</li>
</ol>
<ul>
<li><strong>HTTPS:443</strong>: 아까 ACM에서 만든 인증서 걸어주고, 대상 그룹으로 넘기기</li>
<li><strong>HTTP:80</strong>: 호스트 헤드 =  도메인 , URL로 리디랙션 <pre><code>      -&gt; 모든 요청을 HTTPS로 리디렉션 (상태 코드 301) -&gt; 1순위</code></pre></li>
</ul>
<h4 id="5-route-53---a-레코드-연결">5. Route 53 - A 레코드 연결</h4>
<ol>
<li>Route 53 호스팅 영역 다시 들어감.
1-1. 서브도메인(백엔드용)
1-2. <strong>레코드 생성</strong>: <code>www</code> 혹은 <code>api</code> 등 서브도메인 입력
1-3. <strong>별칭(Alias)</strong>: 스위치 켜고, &#39;ALB에 대한 별칭&#39; 선택해서 만든 로드밸런서 주소랑 연결
1-1. 도메인(프론트용)
1-2. <strong>레코드 생성</strong>: 아무런 이름 입력 X
1-3. <strong>별칭(Alias) 끄기</strong>: ALB에 대한 별칭 해제하고 프론트의 vercel 배포 주소를 넣기</li>
</ol>
<h4 id="6-cors-및-헬스-체크-이슈-해결-중요함">6. CORS 및 헬스 체크 이슈 해결 (중요함!)</h4>
<ul>
<li><strong>CORS 에러</strong>: 프론트(<code>www</code>)랑 백(<code>api</code>) 도메인이 다르기 때문에 스프링 부트에서 허용해줘야 함. <code>allowedOrigins</code>에 프론트 주소 정확히 작성하기</li>
<li><strong>Health Check</strong>: ALB는 주기적으로 서버가 살아있는지 체크해줌 (<code>/</code> 경로가 404 뜨면 서버 죽은 줄 알고 트래픽 끊음)<ul>
<li>스프링 부트에 간단하게 <code>200 OK</code> 뱉는 <code>/health</code> 컨트롤러 만들고, 대상 그룹 설정에서 헬스 체크 경로를 <code>/health</code>로 바꿔서 테스트하기</li>
</ul>
</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[Amazon Linux에서 Nginx 설치 및 설정]]></title>
            <link>https://velog.io/@cse23_ewha/Amazon-Linux%EC%97%90%EC%84%9C-Nginx-%EC%84%A4%EC%B9%98-%EB%B0%8F-%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@cse23_ewha/Amazon-Linux%EC%97%90%EC%84%9C-Nginx-%EC%84%A4%EC%B9%98-%EB%B0%8F-%EC%84%A4%EC%A0%95</guid>
            <pubDate>Sat, 17 Jan 2026 03:03:13 GMT</pubDate>
            <description><![CDATA[<h3 id="🛠️-amazon-linux에서-nginx-설치-및-설정">🛠️ Amazon Linux에서 Nginx 설치 및 설정</h3>
<h4 id="1-nginx-설치">1. Nginx 설치</h4>
<pre><code class="language-bash"># 패키지 매니저 업데이트
sudo yum update -y

# Nginx 설치
sudo yum install nginx -y

# Nginx 실행 및 서버 부팅 시 자동 실행 설정
sudo systemctl start nginx
sudo systemctl enable nginx
</code></pre>
<h4 id="2-설정-파일-수정">2. 설정 파일 수정</h4>
<pre><code class="language-bash"># 기본 설정 파일 백업 
sudo cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak

# 설정 파일 열기
sudo nano /etc/nginx/nginx.conf
</code></pre>
<h4 id="3-nginxconf-내용-수정">3. <code>nginx.conf</code> 내용 수정</h4>
<pre><code class="language-nginx">server {
    listen       80;
    listen       [::]:80;
    server_name  도메인 www.도메인;

    location / {
        proxy_pass http://localhost:8080;
        proxy_http_version 1.1;

        # SSE 및 실시간 통신 설정
        proxy_set_header Connection &quot;&quot;;
        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_cache_off;
        proxy_buffering off;
        proxy_read_timeout 3600s;
        chunked_transfer_encoding off;
    }

    # 기존에 있던 다른 설정들은 일단 그대로 두기
}
</code></pre>
<h4 id="4-적용-및-확인">4. 적용 및 확인</h4>
<pre><code class="language-bash"># 문법 체크 
sudo nginx -t

# Nginx 재시작
sudo systemctl restart nginx
</code></pre>
<hr>
<h4 id="주의사항-aws-보안-그룹">주의사항 (AWS 보안 그룹)</h4>
<p><strong>AWS 보안 그룹</strong>에서 <strong>80 포트</strong>를 여는 것이 필수</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Git Bash에서 RDS DB 세팅하기]]></title>
            <link>https://velog.io/@cse23_ewha/Git-Bash%EC%97%90%EC%84%9C-RDS-DB-%EC%84%B8%ED%8C%85%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@cse23_ewha/Git-Bash%EC%97%90%EC%84%9C-RDS-DB-%EC%84%B8%ED%8C%85%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 16 Jan 2026 07:22:23 GMT</pubDate>
            <description><![CDATA[<h4 id="1-rds-접속-명령어-입력">1. RDS 접속 명령어 입력</h4>
<p>Git Bash를 열고 명령어를 입력</p>
<pre><code class="language-bash">mysql -h &lt;RDS_엔드포인트_주소&gt; -u &lt;마스터_사용자_이름&gt; -p
</code></pre>
<blockquote>
<p><strong>잠깐!</strong> 만약 여기서 무한 로딩시 <strong>AWS RDS 보안 그룹(3306 포트)</strong>에서 <strong>&#39;내 IP&#39;</strong>가 허용되어 있는지 다시 확인</p>
</blockquote>
<h4 id="2-비밀번호-입력">2. 비밀번호 입력</h4>
<p><code>Enter password:</code> 라는 문구가 뜨면 RDS 생성 시 설정했던 <strong>비밀번호</strong>를 입력하고 엔터
(입력할 때 화면에 아무 글자도 안 보이지만 실제로는 입력되고 있는 중..)</p>
<h4 id="3-데이터베이스-생성-및-확인">3. 데이터베이스 생성 및 확인</h4>
<p>접속에 성공해서 <code>mysql&gt;</code> 프롬프트가 보이면 아래 명령어들을 입력</p>
<pre><code class="language-sql">-- 1. 데이터베이스 생성
CREATE DATABASE madcamp;

-- 2. 생성 확인 
SHOW DATABASES;

-- 3. 생성한 DB 선택
USE madcamp;

-- 4. 테이블 생성 SQL

--5. 생성된 테이블 목록 확인
SHOW TABLES;
테이블 이름이 리스트로 쭉 떠야 함

3. 특정 테이블 구조 확인 
(예를 들어 member 테이블이 설계한 대로 잘 만들어졌는지 컬럼을 확인하고싶을때)
DESC member;
</code></pre>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[안드로이드 에뮬레이터]]></title>
            <link>https://velog.io/@cse23_ewha/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EC%97%90%EB%AE%AC%EB%A0%88%EC%9D%B4%ED%84%B0</link>
            <guid>https://velog.io/@cse23_ewha/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EC%97%90%EB%AE%AC%EB%A0%88%EC%9D%B4%ED%84%B0</guid>
            <pubDate>Fri, 09 Jan 2026 03:21:52 GMT</pubDate>
            <description><![CDATA[<h3 id="1단계-디바이스-매니저device-manager-열기">1단계: 디바이스 매니저(Device Manager) 열기</h3>
<ol>
<li>안드로이드 스튜디오 상단 메뉴에서 Tools -&gt; Device Manager를 클릭</li>
<li>또는 오른쪽 사이드바에 있는 기기 모양 아이콘(Device Manager)을 클릭</li>
</ol>
<h3 id="2단계-가상-기기-만들기">2단계: 가상 기기 만들기</h3>
<ol>
<li>Device Manager 창에서 [Create device] (또는 + 버튼)를 누르기</li>
<li>Hardware 선택: 목록에서 원하는 기기를 고르기</li>
</ol>
<ul>
<li>팁: <code>Pixel 7</code>이나 <code>Pixel 8</code>처럼 옆에 Play Store 로고가 있는 기기를 고르기</li>
</ul>
<ol start="3">
<li>기기를 선택했으면 오른쪽 하단 [Next]를 누르기</li>
</ol>
<h3 id="3단계-안드로이드-os-버전-선택">3단계: 안드로이드 OS 버전 선택</h3>
<ol>
<li>가상 핸드폰에 설치할 안드로이드 버전 고르기</li>
<li>추천: <code>API 34</code> (Android 14) 또는 <code>API 33</code> (Android 13)을 권장함</li>
<li>버전 이름 옆에 파란색 [Download] 아이콘이 있다면 먼저 클릭해서 설치하기</li>
<li>다운로드가 끝나면 해당 버전을 선택하고 [Next]를 누르기</li>
</ol>
<h3 id="4단계-설정-마무리">4단계: 설정 마무리</h3>
<ol>
<li>Verify Configuration: 기기 이름을 정하는 단계인데, 기본값으로 두고 [Finish]를 선택</li>
<li>이제 Device Manager 리스트에 방금 만든 기기가 나타남</li>
</ol>
<h3 id="5단계-앱-실행하기">5단계: 앱 실행하기</h3>
<ol>
<li>안드로이드 스튜디오 상단 툴바가 방금 만든 가상 기기 이름으로 선택되어 있는지 확인</li>
<li>그 옆에 있는 초록색 재생 버튼(▶️, Run &#39;app&#39;) 클릭</li>
<li>기다리면 컴퓨터 화면에 핸드폰 모양 창이 뜨면서 안드로이드가 부팅되고 코드가 실행됌</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[V0로 만든디자인을 안드로이드 스튜디오(Java)로 옮기기]]></title>
            <link>https://velog.io/@cse23_ewha/V0%EB%A1%9C-%EB%A7%8C%EB%93%A0%EB%94%94%EC%9E%90%EC%9D%B8%EC%9D%84-%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EC%8A%A4%ED%8A%9C%EB%94%94%EC%98%A4Java%EB%A1%9C-%EC%98%AE%EA%B8%B0%EB%8A%94-%EA%B3%BC%EC%A0%95</link>
            <guid>https://velog.io/@cse23_ewha/V0%EB%A1%9C-%EB%A7%8C%EB%93%A0%EB%94%94%EC%9E%90%EC%9D%B8%EC%9D%84-%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EC%8A%A4%ED%8A%9C%EB%94%94%EC%98%A4Java%EB%A1%9C-%EC%98%AE%EA%B8%B0%EB%8A%94-%EA%B3%BC%EC%A0%95</guid>
            <pubDate>Fri, 09 Jan 2026 03:03:53 GMT</pubDate>
            <description><![CDATA[<p>V0로 만든 디자인을 안드로이드 스튜디오(Java)로 옮기는 과정 
=&gt; 그림을 보고 설계도를 다시 그리는 것</p>
<p>즉, 해야할일 = 디자인의 수치(색상, 간격, 폰트)를 참고해 안드로이드용 XML로 번역하기</p>
<h3 id="1단계-안드로이드-스튜디오-프로젝트-설정">1단계: 안드로이드 스튜디오 프로젝트 설정</h3>
<p>V0의 UI를 구현하기 위해 프로젝트를 먼저 생성</p>
<ol>
<li>New Project → Empty Views Activity를 선택하세요.</li>
</ol>
<p><em>주의:</em> 그냥 Empty Activity는 최신 Compose 방식이라 Java와 맞지 않고 언어선택도 안됌 
(compose는 코틀린에서만 사용) 반드시 Views가 붙은 것을 골라야 XML과 Java를 사용하기_
2. Language를 Java로 설정
3. Build Configuration Language는 Groovy DSL로 선택 (예제가 많음!!)</p>
<hr>
<h3 id="2단계-디자인-에셋색상-폰트-옮기기">2단계: 디자인 에셋(색상, 폰트) 옮기기</h3>
<p>V0에서 사용한 색상값과 폰트를 안드로이드에 미리 등록하기</p>
<ul>
<li>색상 등록: <code>res/values/colors.xml</code> 파일을 열고 기획하신 색상을 추가<pre><code class="language-xml">&lt;resources&gt;
  &lt;color name=&quot;primary&quot;&gt;#1E3A8A&lt;/color&gt;
  &lt;color name=&quot;secondary&quot;&gt;#64748B&lt;/color&gt;
  &lt;color name=&quot;background&quot;&gt;#F8FAFC&lt;/color&gt;
  &lt;color name=&quot;status_stored&quot;&gt;#16A34A&lt;/color&gt;
  &lt;/resources&gt;
</code></pre>
</li>
</ul>
<pre><code>

* 폰트 적용: 1.  `res` 폴더 우클릭 → New → Android Resource Directory → Resource type을 font로 선택
2.  다운로드한 Noto Sans KR 파일(.ttf)을 `res/font` 폴더에 넣기 (파일명은 소문자와 언더바만 가능: `noto_sans_kr.ttf`).
* 테마 설정: `res/values/themes.xml`에서 `android:fontFamily`를 지정하면 앱 전체에 적용됌

---

### 3단계: 화면 구조 잡기 (탭 구성)

&#39;리턴즈&#39; 앱은 하단 탭이 4개이므로 `BottomNavigationView`를 사용해야 합니다.

1. Layout 만들기: `res/layout/activity_main.xml`에서 화면 하단에 하단 바를 배치합니다.
2. Menu 만들기: `res/menu` 폴더를 만들고 4개의 탭(홈, 갤러리, 습득등록, 분실등록) 아이콘과 이름을 정의합니다.
3. Fragment: 각 탭을 클릭했을 때 바뀔 화면 4개를 생성합니다. (우클릭 → New → Fragment → Fragment (Blank))
* `HomeFragment`, `GalleryFragment`, `FoundRegFragment`, `LostRegFragment`



---

### 4단계: XML로 UI 번역하기 (V0 코드 보고 그리기)

V0에서 본 디자인 요소들을 안드로이드 뷰로 바꾸기

| V0 (웹 요소) | 안드로이드 (XML 요소) | 설명 |
| --- | --- | --- |
| `&lt;div&gt;` (박스) | `LinearLayout` 또는 `ConstraintLayout` | 요소를 담는 그릇 |
| `&lt;h1&gt;`, `&lt;p&gt;` | `TextView` | 글자 표시 |
| `&lt;input&gt;` | `EditText` | 입력창 |
| `&lt;img&gt;` | `ImageView` | 사진 |
| `List`, `Flex` | `RecyclerView` | 목록 구현 |
| `Button` | `Button` 또는 `MaterialButton` | 클릭 버튼 |

예시: 탭 1의 검색창 만들기

```xml
&lt;EditText
    android:id=&quot;@+id/search_bar&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;50dp&quot;
    android:hint=&quot;제목 또는 장소 검색&quot;
    android:background=&quot;@drawable/bg_search_rounded&quot; 
    android:padding=&quot;12dp&quot; /&gt;
</code></pre><p><em>(둥근 모서리는 <code>res/drawable</code>에 XML 파일을 따로 만들어 적용...가장 하단 참조)</em></p>
<hr>
<h3 id="5단계-java-코드로-기능-연결하기">5단계: Java 코드로 기능 연결하기</h3>
<p>이제 화면(XML)과 로직(Java)을 연결해야함</p>
<ol>
<li>View 연결: <code>findViewById()</code>를 사용하여 Java 코드에서 XML 부품을 불러오기<pre><code class="language-java">TextView titleText = findViewById(R.id.item_title);
titleText.setText(&quot;파란 우산 습득&quot;);
</code></pre>
</li>
</ol>
<p>```</p>
<ol start="2">
<li>데이터 리스트: 탭 1의 목록을 보여주기 위해 Adapter가 필요
 데이터(리스트) -&gt; 어댑터 -&gt; RecyclerView 순서로 연결</li>
</ol>
<ol start="3">
<li>탭 전환 로직: <code>BottomNavigationView</code>의 아이템 클릭 리스너를 만들어 클릭 시 프래그먼트를 교체(<code>FragmentTransaction</code>)하게 함</li>
</ol>
<hr>
<h3 id="가장-오류-많이-나는-부분">가장 오류 많이 나는 부분</h3>
<ol>
<li>둥근 모서리와 테두리: V0(Tailwind)의 <code>rounded-lg</code> 같은 효과는 안드로이드에서 <code>res/drawable</code>에 <code>shape</code> XML 파일을 만들어야함 (귀찮기도하고 약간 복잡?)</li>
<li>RecyclerView: 리스트 구현이 처음엔 꽤 복잡
 (Item XML, ViewHolder, Adapter 3종 세트가 필요함,, but 한 번 배우면 계속 씀/시작만 어렵다~)</li>
<li>ViewBinding: <code>findViewById</code>를 수백 번 쓰는 대신 <code>ViewBinding</code>이라는 기술을 사용하면 코드가 훨씬 깨끗해짐</li>
</ol>
<hr>
]]></description>
        </item>
    </channel>
</rss>