<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>siha_014.log</title>
        <link>https://velog.io/</link>
        <description>뭐라도 해보자</description>
        <lastBuildDate>Sat, 18 Apr 2026 12:08:41 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>siha_014.log</title>
            <url>https://velog.velcdn.com/images/siha_014/profile/4d39fac2-ff1c-4ab8-ab2f-225fe69a3252/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. siha_014.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/siha_014" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Spring SSE구현]]></title>
            <link>https://velog.io/@siha_014/Spring-SSE%EA%B5%AC%ED%98%84</link>
            <guid>https://velog.io/@siha_014/Spring-SSE%EA%B5%AC%ED%98%84</guid>
            <pubDate>Sat, 18 Apr 2026 12:08:41 GMT</pubDate>
            <description><![CDATA[<h1 id="sseserver-sent-events란">SSE(Server-Sent Events)란</h1>
<p>프로젝트를 구현하는 중, OpenAI API와의 채팅 기능을 개발하게 되었다. 실시간으로 AI 응답을 스트리밍해야 했고, 자연스럽게 WebSocket과 SSE 중 하나를 선택해야 하는 상황에 놓였다. 결론적으로 SSE를 선택했다. 이 글에서는 WebSocket과 SSE를 비교하고, 왜 SSE를 선택하게 되었는지 정리하고자 한다.</p>
<hr>
<h2 id="1-웹의-실시간-통신">1. 웹의 실시간 통신</h2>
<p>HTTP는 기본적으로 클라이언트와 서버 간의 <strong>요청(Request) / 응답(Response)</strong> 모델로 동작한다. 클라이언트가 요청을 보내면, 서버는 그에 대한 응답을 반환한다. 응답이 완료되면 연결은 종료된다.</p>
<p>이 구조는 단순하고 명확하지만, <strong>실시간성이 필요한 상황</strong>에서는 한계가 있다.</p>
<h3 id="polling">Polling</h3>
<p>이를 해결하기 위한 가장 단순한 방법이 <strong>Polling</strong>이다. Polling은 클라이언트가 일정 주기로 서버에 요청을 보내 상태를 확인하는 방식이다.</p>
<pre><code>클라이언트 → 서버: &quot;new data?&quot;  (매 N초마다 반복)
서버 → 클라이언트: &quot;yes&quot; or &quot;no&quot;</code></pre><p>구현 방식이 가장 간단하지만, 명확한 단점이 있다.</p>
<ul>
<li><strong>서버 리소스 낭비</strong>: 변화가 없어도 주기적으로 요청이 발생한다.</li>
<li><strong>실시간성 부족</strong>: 이벤트 발생 시점과 클라이언트가 데이터를 받는 시점이 일치하지 않는다.</li>
</ul>
<p>이러한 단점을 해결하기 위해 등장한 것이 <strong>WebSocket</strong>과 <strong>SSE</strong>다.</p>
<hr>
<h2 id="2-websocket--sse">2. WebSocket &amp; SSE</h2>
<h3 id="websocket">WebSocket</h3>
<p>WebSocket은 <strong>양방향(Full-Duplex) 통신</strong> 기술이다. 일반 HTTP와 달리 <strong>Stateful</strong>하며, 한 번 연결이 수립되면 클라이언트와 서버가 서로 필요할 때마다 자유롭게 메시지를 주고받을 수 있다.</p>
<p>연결 수립 시 HTTP <strong>Handshake</strong>를 통해 WebSocket 프로토콜로 업그레이드한다.</p>
<pre><code>클라이언트 → 서버: HTTP Upgrade 요청
서버 → 클라이언트: 101 Switching Protocols
--- 이후 WebSocket 연결 유지 ---
클라이언트 ↔ 서버: 자유로운 양방향 메시지</code></pre><p><strong>주요 특징:</strong></p>
<ul>
<li>양방향 통신</li>
<li>지속적인 연결 유지 (Stateful)</li>
<li>실시간 채팅, 게임, 협업 도구에 적합</li>
</ul>
<h3 id="sse-server-sent-events">SSE (Server-Sent Events)</h3>
<p>SSE는 <strong>서버 → 클라이언트 단방향 스트리밍</strong> 기술이다. HTTP 연결을 유지한 채로, 서버가 클라이언트에게 데이터를 지속적으로 흘려보낼 수 있다.</p>
<pre><code>클라이언트 → 서버: 연결 요청 (한 번)
서버 → 클라이언트: 데이터 스트리밍 (지속적)</code></pre><p>응답 형식은 <code>data:</code> prefix를 가진 텍스트 스트림이다.</p>
<pre><code>data: {&quot;content&quot;: &quot;Hello&quot;}
data: {&quot;content&quot;: &quot; world&quot;}
data: [DONE]</code></pre><p><strong>주요 특징:</strong></p>
<ul>
<li>서버 → 클라이언트 단방향</li>
<li>HTTP 기반 (별도 프로토콜 업그레이드 불필요)</li>
<li>자동 재연결 지원</li>
<li>구현이 WebSocket보다 단순</li>
</ul>
<hr>
<h2 id="3-websocket-vs-sse">3. WebSocket vs SSE</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>WebSocket</th>
<th>SSE</th>
</tr>
</thead>
<tbody><tr>
<td>통신 방향</td>
<td>양방향</td>
<td>단방향 (서버 → 클라이언트)</td>
</tr>
<tr>
<td>프로토콜</td>
<td>ws:// / wss://</td>
<td>HTTP</td>
</tr>
<tr>
<td>연결 방식</td>
<td>Handshake 후 업그레이드</td>
<td>일반 HTTP 연결 유지</td>
</tr>
<tr>
<td>상태</td>
<td>Stateful</td>
<td>HTTP 기반</td>
</tr>
<tr>
<td>자동 재연결</td>
<td>직접 구현</td>
<td>브라우저 내장 지원</td>
</tr>
<tr>
<td>구현 복잡도</td>
<td>높음</td>
<td>낮음</td>
</tr>
<tr>
<td>적합한 상황</td>
<td>실시간 채팅, 게임, 협업</td>
<td>알림, 피드, AI 스트리밍</td>
</tr>
</tbody></table>
<hr>
<h2 id="4-sse를-선택한-이유">4. SSE를 선택한 이유</h2>
<p>이 프로젝트의 통신 구조를 분석해보면 다음과 같다.</p>
<pre><code>클라이언트 → 서버: 사용자 메시지 전송 (HTTP POST)
서버 → 클라이언트: AI 응답 스트리밍 (실시간)</code></pre><p>클라이언트 → 서버 방향은 단순한 HTTP POST로 충분하다. 서버가 클라이언트에게 먼저 메시지를 <strong>자발적으로</strong> 보내야 하는 상황이 없기 때문이다.</p>
<p>결국 <strong>서버 → 클라이언트 방향의 스트리밍만 필요</strong>하다. WebSocket의 양방향 기능은 이 프로젝트에서 불필요한 복잡도를 추가할 뿐이다.</p>
<blockquote>
<p>단방향 스트리밍 특성상 WebSocket보다 SSE가 더 적합하다.</p>
</blockquote>
<hr>
<h2 id="5-spring-webflux에서-sse-구현">5. Spring WebFlux에서 SSE 구현</h2>
<p>SSE 스트리밍 응답을 처리하기 위해 Spring WebFlux의 <code>Flux</code>를 활용했다.</p>
<h3 id="의존성-추가">의존성 추가</h3>
<pre><code class="language-groovy">implementation &#39;org.springframework.boot:spring-boot-starter-webflux&#39;</code></pre>
<h3 id="controller">Controller</h3>
<p><code>produces = MediaType.TEXT_EVENT_STREAM_VALUE</code>를 설정하면 Spring이 자동으로 SSE 형식으로 응답한다.</p>
<pre><code class="language-java">@PostMapping(value = &quot;/{sessionId}/messages&quot;, produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux&lt;String&gt; sendMessage(
        @AuthenticationPrincipal CustomOAuth2User principal,
        @PathVariable Long sessionId,
        @RequestBody SendMessageRequest dto
) {
    return conversationService.sendMessage(principal.getUserId(), sessionId, dto);
}</code></pre>
<h3 id="service---openai-스트리밍">Service - OpenAI 스트리밍</h3>
<p>OpenAI API에 <code>&quot;stream&quot;: true</code>를 설정하고, <code>bodyToFlux(String.class)</code>로 응답을 스트림으로 받는다.</p>
<pre><code class="language-java">public Flux&lt;String&gt; stream(List&lt;ChatMessage&gt; conversationHistory, String userMessage) {
    Map&lt;String, Object&gt; requestBody = Map.of(
            &quot;model&quot;, model,
            &quot;max_tokens&quot;, maxTokens,
            &quot;stream&quot;, true,
            &quot;messages&quot;, buildMessages(PromptConstants.CHAT_PROMPT, conversationHistory, userMessage)
    );

    return openAiWebClient.post()
            .uri(&quot;/chat/completions&quot;)
            .bodyValue(requestBody)
            .retrieve()
            .bodyToFlux(String.class)
            .filter(chunk -&gt; !chunk.equals(&quot;[DONE]&quot;))
            .mapNotNull(this::extractStreamContent);
}</code></pre>
<h3 id="스트리밍-응답-수집-및-저장">스트리밍 응답 수집 및 저장</h3>
<p>스트리밍으로 오는 조각들을 <code>doOnNext</code>로 모으고, 완료 시점에 <code>doOnComplete</code>로 DB에 저장한다.</p>
<pre><code class="language-java">StringBuilder fullResponse = new StringBuilder();

return openAiService.stream(history, dto.getContent())
        .doOnNext(fullResponse::append)
        .doOnComplete(() -&gt; {
            saveAssistantMessage(sessionId, fullResponse.toString());
            conversationRedisService.addMessage(sessionId, ChatMessage.ofAssistant(fullResponse.toString()));
        });</code></pre>
<p>클라이언트 입장에서는 조각조각 실시간으로 받고, 서버는 모든 조각이 도착했을 때 전체 응답을 저장하는 구조다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[OAuth2로 구글로그인 구현하기]]></title>
            <link>https://velog.io/@siha_014/OAuth2%EB%A1%9C-%EA%B5%AC%EA%B8%80%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@siha_014/OAuth2%EB%A1%9C-%EA%B5%AC%EA%B8%80%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 15 Apr 2026 05:10:10 GMT</pubDate>
            <description><![CDATA[<h1 id="spring-security-oauth2-구글-로그인-구현하기">Spring Security OAuth2 구글 로그인 구현하기</h1>
<p>현재 프로젝트에서 구글 로그인과 일반 로그인을 같이 사용하려고 한다.
또, 해당 프로젝트에서는 Access Token + Refresh Token의 JWT 인증 방식을 사용하고 있어, 이에 맞춰야 했다.</p>
<hr>
<h2 id="google-로그인-흐름">Google 로그인 흐름</h2>
<p><img src="https://velog.velcdn.com/images/siha_014/post/354c28ef-1eb2-4c42-8166-4ba1032db7ff/image.png" alt=""></p>
<ol>
<li>클라이언트가 구글에 로그인을 요청한다.</li>
<li>구글이 인가 코드를 반환한다.</li>
<li>클라이언트가 인가 코드를 서버에 전달한다.<ul>
<li>서버가 직접 구글에 Access Token을 요청하기 위함이다. 브라우저에 Access Token을 직접 노출하지 않아 보안상 안전하다.</li>
</ul>
</li>
<li>인가 코드를 전달받은 서버가 인가 코드와 함께 Access Token을 요청한다.</li>
<li>구글은 서버가 안전하다고 판단할 시 Access Token을 반환한다.</li>
<li>서버가 Access Token으로 사용자 정보를 요청한다.</li>
<li>구글이 사용자 정보를 반환한다. (<code>email</code>, <code>name</code>, <code>sub</code> 등)</li>
<li>이를 기반으로 회원을 등록하거나 조회하고 JWT 로직을 실행한다.</li>
<li>Access Token과 Refresh Token과 함께 클라이언트로 리다이렉트한다.</li>
</ol>
<blockquote>
<p><strong>참고</strong>: 4~7번 과정은 Spring Security가 자동으로 처리한다.
<code>CustomOAuth2UserService.loadUser()</code>가 호출될 때 이미 완료된 상태이다.</p>
</blockquote>
<hr>
<h2 id="엔티티">엔티티</h2>
<h3 id="user">User</h3>
<pre><code class="language-java">@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = &quot;users&quot;)
public class User extends Timestamped {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, length = 50)
    private String nickname;

    @Column(nullable = false, unique = true, length = 100)
    private String email;

    @Column
    private String password;

    @Enumerated(EnumType.STRING)
    @Column(nullable = false)
    private Role role;

    @Column(nullable = false)
    private Integer streakDays = 0;

    @Column
    private LocalDate lastStudiedAt;

    @Builder
    public User(String nickname, String email, String password, Role role) {
        this.nickname = nickname;
        this.email = email;
        this.password = password;
        this.role = role;
    }

    public void updateNickname(String nickname) { this.nickname = nickname; }
}</code></pre>
<h3 id="oauthaccount">OAuthAccount</h3>
<pre><code class="language-java">@Entity
@Table(name = &quot;oauth_accounts&quot;,
        uniqueConstraints = @UniqueConstraint(columnNames = {&quot;provider&quot;, &quot;provider_id&quot;}))
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class OAuthAccount {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = &quot;user_id&quot;, nullable = false)
    private User user;

    @Column(nullable = false, length = 20)
    private String provider;

    @Column(name = &quot;provider_id&quot;, nullable = false, length = 1000)
    private String providerId;

    @CreatedDate
    @Column(nullable = false, updatable = false)
    private LocalDateTime createdAt;

    @Builder
    public OAuthAccount(User user, String provider, String providerId) {
        this.user = user;
        this.provider = provider;
        this.providerId = providerId;
    }
}</code></pre>
<p><code>User</code> 엔티티에 <code>provider</code>와 <code>providerId</code>를 nullable로 넣지 않고, <code>OAuthAccount</code>로 분리한 이유는 확장성 때문이다.
한 유저가 여러 OAuth 계정(구글, 카카오 등)을 연결할 수 있도록 고려했고, 일반 로그인과 확실히 구분하기 위해 이러한 선택을 했다.</p>
<hr>
<h2 id="customoauth2user">CustomOAuth2User</h2>
<p>인증된 사용자 정보(principal)를 담는 객체이다. <code>SecurityContext</code>의 <code>Authentication</code>에서 principal로 사용된다.</p>
<p>이 프로젝트는 구글 로그인과 일반 로그인을 모두 지원하기 때문에, 두 방식 모두 동일한 principal 타입을 사용해 일관성을 유지했다.</p>
<pre><code class="language-java">@Getter
public class CustomOAuth2User implements OAuth2User {

    private final User user;
    private final Map&lt;String, Object&gt; attributes;

    public CustomOAuth2User(User user, Map&lt;String, Object&gt; attributes) {
        this.user = user;
        this.attributes = attributes;
    }

    @Override
    public Map&lt;String, Object&gt; getAttributes() {
        return attributes;
    }

    @Override
    public Collection&lt;? extends GrantedAuthority&gt; getAuthorities() {
        return List.of(new SimpleGrantedAuthority(&quot;ROLE_&quot; + user.getRole().name()));
    }

    @Override
    public String getName() {
        return String.valueOf(user.getId());
    }
}</code></pre>
<ul>
<li><code>user</code>: 우리 서비스 DB에 저장된 사용자 정보</li>
<li><code>attributes</code>: 구글로부터 받은 원본 사용자 정보 (OAuth2 로그인 시 채워짐, JWT 인증 시 빈 Map)</li>
</ul>
<hr>
<h2 id="customoauth2userservice">CustomOAuth2UserService</h2>
<p><code>DefaultOAuth2UserService</code>를 상속받아 구글 로그인 시 사용자 정보를 처리하는 서비스이다.</p>
<pre><code class="language-java">@Service
@RequiredArgsConstructor
public class CustomOAuth2UserService extends DefaultOAuth2UserService {

    private final UserRepository userRepository;
    private final OAuthAccountRepository oAuthAccountRepository;

    @Override
    @Transactional
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
        OAuth2User oAuth2User = super.loadUser(userRequest);

        String provider = userRequest.getClientRegistration().getRegistrationId();
        Map&lt;String, Object&gt; attributes = oAuth2User.getAttributes();

        String providerId = (String) attributes.get(&quot;sub&quot;);
        String email = (String) attributes.get(&quot;email&quot;);
        String nickname = (String) attributes.get(&quot;name&quot;);

        User user = oAuthAccountRepository.findByProviderAndProviderId(provider, providerId)
                .map(OAuthAccount::getUser)
                .orElseGet(() -&gt; registerNewUser(email, nickname, provider, providerId));

        return new CustomOAuth2User(user, attributes);
    }

    private User registerNewUser(String email, String nickname, String provider, String providerId) {
        User user = userRepository.findByEmail(email)
                .orElseGet(() -&gt; userRepository.save(
                        User.builder()
                                .email(email)
                                .nickname(nickname)
                                .role(Role.USER)
                                .build()
                ));

        oAuthAccountRepository.save(OAuthAccount.builder()
                .user(user)
                .provider(provider)
                .providerId(providerId)
                .build());

        return user;
    }
}</code></pre>
<h3 id="superloaduser가-하는-일">super.loadUser()가 하는 일</h3>
<p><code>super.loadUser()</code>는 <code>DefaultOAuth2UserService</code>의 구현으로, 내부적으로 다음을 수행한다.</p>
<pre><code class="language-java">@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
    // ...
    OAuth2AccessToken token = userRequest.getAccessToken();
    Map&lt;String, Object&gt; attributes = this.attributesConverter.convert(userRequest).convert(response.getBody());
    // ...
    return new DefaultOAuth2User(authorities, attributes, userNameAttributeName);
}</code></pre>
<p><code>userRequest</code> 안에 구글 Access Token이 담겨 있고, 이를 이용해 구글 사용자 정보 API를 호출한다.
즉, <code>super.loadUser()</code> 한 줄로 구글 API 호출과 사용자 정보 파싱이 완료된다.</p>
<hr>
<h2 id="oauth2successhandler">OAuth2SuccessHandler</h2>
<p>구글 로그인이 성공했을 때 동작하는 핸들러이다.
<code>CustomOAuth2UserService.loadUser()</code>가 성공적으로 완료된 후 호출되며, JWT를 발급하고 클라이언트로 리다이렉트한다.</p>
<pre><code class="language-java">@Component
@RequiredArgsConstructor
public class OAuth2SuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

    private final JwtProvider jwtProvider;
    private final RefreshTokenService refreshTokenService;

    @Value(&quot;${app.oauth2.redirect-uri}&quot;)
    private String redirectUri;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                                        Authentication authentication) throws IOException, ServletException {
        CustomOAuth2User oAuth2User = (CustomOAuth2User) authentication.getPrincipal();
        Long userId = oAuth2User.getUser().getId();

        String accessToken = jwtProvider.generateAccessToken(userId);
        String refreshToken = jwtProvider.generateRefreshToken(userId);

        refreshTokenService.save(userId, refreshToken);

        String targetUrl = UriComponentsBuilder.fromUriString(redirectUri)
                .queryParam(&quot;accessToken&quot;, accessToken)
                .queryParam(&quot;refreshToken&quot;, refreshToken)
                .build().toUriString();

        getRedirectStrategy().sendRedirect(request, response, targetUrl);
    }
}</code></pre>
<p><code>authentication.getPrincipal()</code>로 <code>CustomOAuth2User</code>를 꺼내 <code>userId</code>를 가져온다.
이후 JWT를 발급하고, Refresh Token은 Redis에 저장한 뒤 클라이언트로 리다이렉트한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CustomOAuth2User 구현하기]]></title>
            <link>https://velog.io/@siha_014/CustomOAuth2User-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@siha_014/CustomOAuth2User-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 13 Apr 2026 07:56:32 GMT</pubDate>
            <description><![CDATA[<p>프로젝트를 구현하면서 OAuth2를 사용하기로 결정한 후, 어떻게 인증을 구현할 것인지 고민했다.
우선 이 방식에서는 기존에 사용하던 UserDetails 구현체인 CustomUserDetails를 그대로 사용하는 것이 적절한지 고민이 되었다.</p>
<p>결론적으로,
OAuth2 로그인 과정에서는 UserDetails보다는 OAuth2User를 사용하는 것이 더 적절하다고 판단했다.</p>
<p>그 이유는 다음과 같다.</p>
<h2 id="왜-userdetails가-아니라-oauth2user인가">왜 UserDetails가 아니라 OAuth2User인가?</h2>
<p>UserDetails는 username/password 기반의 인증을 전제로 설계된 인터페이스이다.
즉, 서버가 직접 사용자의 인증 정보를 검증하는 일반 로그인 방식에 적합하다.</p>
<p>반면 OAuth2 로그인은 다음과 같은 흐름을 가진다.</p>
<blockquote>
<p>사용자 → Google 로그인 → Google이 인증 → 사용자 정보 반환</p>
</blockquote>
<p>이 과정에서 서버는 인증을 직접 수행하지 않고,
외부 Provider(Google)로부터 사용자 정보를 전달받는다.</p>
<p>이때 전달되는 데이터는 다음과 같은 형태이다.</p>
<pre><code>{
  &quot;email&quot;: &quot;...&quot;,
  &quot;name&quot;: &quot;...&quot;,
  &quot;picture&quot;: &quot;...&quot;
}</code></pre><p>=&gt; 즉, 정형화된 필드가 아니라 Map 형태의 attributes 데이터</p>
<p>따라서 이러한 구조를 다루기 위해 Spring Security에서는 <code>getAttributes()</code>를 제공하는 <code>OAuth2User</code> 인터페이스를 사용한다.</p>
<h2 id="customoauth2user">CustomOAuth2User</h2>
<p>이러한 이유로, 본 프로젝트에서는 OAuth2 로그인 시 전달받은 사용자 정보와 DB의 User 엔티티를 함께 다루기 위해 <code>CustomOAuth2User</code>를 구현하였다.</p>
<pre><code class="language-java">@Getter
public class CustomOAuth2User implements OAuth2User {

    private final User user;
    private final Map&lt;String, Object&gt; attributes;

    public CustomOAuth2User(User user, Map&lt;String, Object&gt; attributes) {
        this.user = user;
        this.attributes = attributes;
    }

    @Override
    public Map&lt;String, Object&gt; getAttributes() {
        return attributes;
    }

    @Override
    public Collection&lt;? extends GrantedAuthority&gt; getAuthorities() {
        return List.of(new SimpleGrantedAuthority(&quot;ROLE_&quot; + user.getRole().name()));
    }

    @Override
    public String getName() {
        return String.valueOf(user.getId());
    }
}</code></pre>
<ul>
<li><code>user</code> -&gt; 우리 서비스의 DB에 저장된 사용자 정보</li>
<li><code>attributes</code> -&gt; OAuth2 Provider(Google)로부터 받은 원본 사용자 정보</li>
</ul>
<p>=&gt; 내부 사용자 정보 + 외부 인증 정보를 하나의 객체로 통합</p>
<h2 id="jwtauthenticationfilter에서의-처리">JwtAuthenticationFilter에서의 처리</h2>
<p>OAuth2 로그인이 완료된 이후에는 JWT 기반 인증을 사용하므로,
요청마다 JWT 토큰을 검증하고 사용자 정보를 복원해야 한다.</p>
<pre><code>CustomOAuth2User oAuth2User = new CustomOAuth2User(user, Map.of());</code></pre><p>여기서 attributes를 Map.of()로 비워두는 이유는 다음과 같다.</p>
<p>=&gt; JWT 인증 과정에서는 이미 로그인 과정이 끝난 상태이기 때문에
=&gt; 더 이상 OAuth2 Provider로부터 받은 원본 데이터는 필요하지 않기 때문이다.</p>
<p>즉,</p>
<ul>
<li>OAuth 로그인 시 → attributes 필요</li>
<li>JWT 인증 시 → user 정보만 필요</li>
</ul>
<p>이후 <code>CustomOAuth2User</code>를 principal로 갖는 Authentication 객체를 생성하여
SecurityContext에 저장한다.</p>
<pre><code class="language-java">Authentication authentication = new UsernamePasswordAuthenticationToken(
        oAuth2User, null, oAuth2User.getAuthorities()
);</code></pre>
<p>이를 통해 Controller에서는 다음과 같이 사용자 정보를 사용할 수 있다.</p>
<pre><code class="language-java">@AuthenticationPrincipal CustomOAuth2User user</code></pre>
<h2 id="정리">정리</h2>
<ul>
<li><code>UserDetails</code>는 서버가 직접 인증하는 일반 로그인 방식에 적합하다.</li>
<li><code>OAuth2User</code>는 외부 Provider로부터 전달받은 사용자 정보를 처리하기 위한 인터페이스이다.</li>
<li>JWT 인증 단계에서는 두 인터페이스 중 어떤 것을 사용해도 무방하지만,
본 프로젝트에서는 OAuth2 로그인과의 일관성을 유지하기 위해 <code>OAuth2User</code>를 사용하였다.
한 줄 결론</li>
</ul>
<p>=&gt; <strong>OAuth2라서 OAuth2User를 써야 하는 것이 아니라, 외부 인증 데이터(attributes)를 다루고 구조 일관성을 유지하기 위해 선택했다.</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[로컬 MySQL 포트 충돌 해결기]]></title>
            <link>https://velog.io/@siha_014/%EB%A1%9C%EC%BB%AC-MySQL-%ED%8F%AC%ED%8A%B8-%EC%B6%A9%EB%8F%8C-%ED%95%B4%EA%B2%B0%EA%B8%B0</link>
            <guid>https://velog.io/@siha_014/%EB%A1%9C%EC%BB%AC-MySQL-%ED%8F%AC%ED%8A%B8-%EC%B6%A9%EB%8F%8C-%ED%95%B4%EA%B2%B0%EA%B8%B0</guid>
            <pubDate>Wed, 25 Feb 2026 13:03:03 GMT</pubDate>
            <description><![CDATA[<h1 id="mac에서-docker-mysql-연결-안-될-때--로컬-mysql-포트-충돌-해결기">Mac에서 Docker MySQL 연결 안 될 때 — 로컬 MySQL 포트 충돌 해결기</h1>
<h2 id="문제-상황">문제 상황</h2>
<p>Spring Boot 프로젝트에서 Docker Compose로 MySQL을 띄우고 연결하려 했는데, 아래 에러가 계속 발생했다.</p>
<pre><code>Caused by: java.sql.SQLSyntaxErrorException: Access denied for user &#39;dev&#39;@&#39;localhost&#39; to database &#39;ai_english&#39;</code></pre><pre><code>Caused by: com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure</code></pre><p>권한 부여(<code>GRANT ALL PRIVILEGES</code>)도 해보고, IP를 <code>172.18.0.2</code>로 직접 지정도 해봤지만 근본적으로 해결되지 않았다.</p>
<hr>
<h2 id="원인">원인</h2>
<pre><code class="language-bash">docker-compose up -d</code></pre>
<p>를 실행했을 때 아래 에러가 나왔다.</p>
<pre><code>Error response from daemon: ports are not available: exposing port TCP 127.0.0.1:3306 -&gt; 127.0.0.1:0: listen tcp4 127.0.0.1:3306: bind: address already in use</code></pre><p><strong>Mac에 MySQL이 직접 설치되어 있었고, 이미 3306 포트를 점유하고 있었다.</strong></p>
<p>Docker MySQL도 3306 포트를 사용하려 했기 때문에 충돌이 발생했고, Docker 컨테이너가 제대로 뜨지 않았던 것이다.</p>
<pre><code class="language-bash">brew services list | grep mysql
# mysql started ...</code></pre>
<hr>
<h2 id="해결-방법">해결 방법</h2>
<h3 id="방법-1-로컬-mysql-중지-권장">방법 1. 로컬 MySQL 중지 (권장)</h3>
<pre><code class="language-bash">brew services stop mysql
docker-compose up -d</code></pre>
<p>Docker로 MySQL을 관리할 것이라면 로컬 MySQL은 꺼두는 것이 깔끔하다.</p>
<h3 id="방법-2-docker-mysql-포트-변경">방법 2. Docker MySQL 포트 변경</h3>
<p><code>docker-compose.yml</code>에서 포트를 변경한다.</p>
<pre><code class="language-yaml">mysql:
  ports:
    - &quot;3307:3306&quot;</code></pre>
<p><code>application.yml</code>도 맞춰서 변경한다.</p>
<pre><code class="language-yaml">spring:
  datasource:
    url: jdbc:mysql://localhost:3307/ai_english</code></pre>
<p>나는 방법1, 로컬 MySQL을 중지해서 해결했다.</p>
<hr>
<h2 id="교훈">교훈</h2>
<ul>
<li>Mac에 MySQL이 설치되어 있다면 Docker MySQL과 <strong>3306 포트 충돌</strong>이 발생한다.</li>
<li><code>Access denied</code>, <code>Communications link failure</code> 에러가 권한 문제처럼 보여도 <strong>실제로는 컨테이너 자체가 제대로 안 뜬 것</strong>일 수 있다.</li>
<li>문제 해결 시 에러 메시지만 보지 말고 <strong><code>docker ps</code>로 컨테이너 상태부터 확인</strong>하는 것이 중요하다.</li>
</ul>
<p>당연하게 MySQL을 설치, 실행한 것이 문제가 될 줄은 몰랐다.</p>
<pre><code class="language-bash"># 컨테이너 상태 확인 습관 들이기
docker ps
docker-compose up -d</code></pre>
<hr>
<h2 id="개발-환경">개발 환경</h2>
<ul>
<li>macOS</li>
<li>Docker Desktop</li>
<li>Spring Boot 3.5.x</li>
<li>MySQL 8.0 (Docker)</li>
<li><code>brew</code> 로 로컬 MySQL 설치되어 있던 상태</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Board 프로젝트 - Image 업로드 및 콘텐츠 관리 구조]]></title>
            <link>https://velog.io/@siha_014/Board-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-Image-%EC%97%85%EB%A1%9C%EB%93%9C-%EB%B0%8F-%EC%BD%98%ED%85%90%EC%B8%A0-%EA%B4%80%EB%A6%AC-%EA%B5%AC%EC%A1%B0</link>
            <guid>https://velog.io/@siha_014/Board-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-Image-%EC%97%85%EB%A1%9C%EB%93%9C-%EB%B0%8F-%EC%BD%98%ED%85%90%EC%B8%A0-%EA%B4%80%EB%A6%AC-%EA%B5%AC%EC%A1%B0</guid>
            <pubDate>Thu, 19 Feb 2026 06:48:26 GMT</pubDate>
            <description><![CDATA[<h1 id="이미지-업로드와-게시글-저장을-분리한-이유">이미지 업로드와 게시글 저장을 분리한 이유</h1>
<p>WYSIWYG 에디터를 프론트엔드에서 사용한다는 가정하에 백엔드를 구현하고자 하니 고민이 생겼다.</p>
<p>이미지를 어떻게 처리할까?</p>
<p>가장 단순한 방법은 게시글 저장 시 이미지를 함께 전송하는 것이다. 하지만 WYSIWYG 에디터 특성상 사용자는 글을 쓰는 도중에 이미지를 첨부하고, 미리보기로 확인하면서 작성을 이어간다. 이 흐름을 자연스럽게 지원하려면 <strong>이미지 업로드와 게시글 저장을 분리</strong>하는 것이 맞다고 판단했다.</p>
<hr>
<h2 id="실제-동작-흐름">실제 동작 흐름</h2>
<p>사용자 입장에서는 저장 버튼 하나로 모든 게 업로드되는 것처럼 보이지만, 실제로는 두 단계로 나뉜다.</p>
<p><strong>1단계 — 이미지 업로드</strong></p>
<p>WYSIWYG 에디터에 이미지를 첨부하는 순간, 백그라운드에서 먼저 파일이 서버로 전송된다.</p>
<pre><code>POST /api/posts/media/images
Content-Type: multipart/form-data</code></pre><p>서버는 파일을 로컬에 저장하고 UUID 기반의 key와 접근 URL을 반환한다.</p>
<pre><code class="language-json">{
  &quot;key&quot;: &quot;posts/2026/02/11/abc.jpeg&quot;,
  &quot;url&quot;: &quot;/files/posts/2026/02/11/abc.jpeg&quot;
}</code></pre>
<p><img src="https://velog.velcdn.com/images/siha_014/post/47e2c133-1bd7-4aaa-8137-dc60fd63c2f2/image.png" alt=""></p>
<p>에디터는 이 URL을 받아서 HTML에 <code>&lt;img&gt;</code> 태그로 즉시 삽입한다. 덕분에 사용자는 이미지가 본문에 렌더링되는 걸 바로 확인할 수 있다.</p>
<p><strong>2단계 — 게시글 저장</strong></p>
<p>저장 버튼을 누르면 에디터의 HTML 문자열이 JSON으로 전송된다.</p>
<pre><code>POST /api/posts
Content-Type: application/json</code></pre><pre><code class="language-json">{
  &quot;title&quot;: &quot;이미지 테스트&quot;,
  &quot;contents&quot;: &quot;&lt;p&gt;본문 내용&lt;/p&gt;&lt;img src=\&quot;/files/posts/2026/02/11/abc.jpeg\&quot;&gt;&quot;
}</code></pre>
<p><img src="https://velog.velcdn.com/images/siha_014/post/164ee284-c8c9-4862-87c3-b8f6fc5bf4ad/image.png" alt=""></p>
<p>이미지는 이미 업로드가 끝난 상태이므로, 게시글 저장 단계에서는 HTML 문자열만 저장하면 된다.</p>
<hr>
<h2 id="이미지-메타데이터는-어떻게-관리하나">이미지 메타데이터는 어떻게 관리하나</h2>
<p>게시글 HTML 안에 이미지 URL이 포함되어 있긴 하지만, 이것만으로는 &quot;이 게시글에 어떤 이미지가 사용됐는지&quot;를 서버가 파악하기 어렵다. 그래서 <code>PostImage</code>라는 별도 엔티티에 이미지 정보를 저장한다.</p>
<pre><code class="language-java">@Entity
public class PostImage {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = &quot;post_id&quot;, nullable = false)
    private Post post;

    @Column(nullable = false, length = 500)
    private String storageKey;

    @Column(nullable = false)
    private int sortOrder;
}</code></pre>
<p>몇 가지 설계 기준이 있었다.</p>
<p>URL 대신 <code>storageKey</code>만 저장한다. URL은 스토리지 구현에 따라 바뀔 수 있다. 지금은 로컬 파일 서버를 쓰지만 나중에 S3로 전환하면 URL 형식이 달라진다. key만 저장해두면 URL은 런타임에 조립하면 그만이다.</p>
<p><code>sortOrder</code>로 순서를 보장한다. WYSIWYG 에디터에서는 이미지 순서가 의미를 가질 수 있다. HTML 파싱 순서대로 index를 부여해서 저장한다.</p>
<hr>
<h2 id="html에서-이미지를-추출하는-방법">HTML에서 이미지를 추출하는 방법</h2>
<p>게시글이 저장될 때, 서버는 HTML을 분석해서 사용된 이미지 목록을 뽑아낸다.</p>
<pre><code class="language-java">private static final Pattern IMG_SRC_PATTERN =
    Pattern.compile(&quot;&lt;img[^&gt;]*\\s+src=[\&quot;&#39;](/files/[^\&quot;&#39;]+)[\&quot;&#39;][^&gt;]*&gt;&quot;, Pattern.CASE_INSENSITIVE);</code></pre>
<p><code>/files/</code>로 시작하는 URL만 인식하도록 제한했다. 외부 이미지 URL(예: 다른 사이트에서 복붙한 이미지)은 무시한다. 이렇게 하면 서버가 관리하는 이미지만 <code>PostImage</code>에 기록된다.</p>
<hr>
<h2 id="게시글-수정-시-동기화">게시글 수정 시 동기화</h2>
<p>수정이 까다로운 부분이었다. 기존 이미지 중 일부는 삭제되고, 새 이미지가 추가될 수 있다. 순서도 바뀔 수 있다.</p>
<p>여러 방식을 고민했는데, 결국 <strong>전체 삭제 후 재생성</strong> 방식을 택했다.</p>
<ol>
<li>기존 <code>PostImage</code> 전부 삭제</li>
<li>수정된 HTML에서 이미지 추출</li>
<li>새 목록으로 재생성</li>
</ol>
<p>diff 방식도 고려했지만, WYSIWYG 에디터에서는 사용자가 이미지를 자유롭게 이동/삭제/추가하기 때문에 변경분을 추적하는 게 생각보다 복잡해진다. 전체 재생성이 단순하고 버그 여지도 적었다.</p>
<hr>
<h2 id="업로드를-분리한-이유">업로드를 분리한 이유</h2>
<p>WYSIWYG 에디터는 사용자가 이미지를 붙이는 즉시 본문에서 미리보기를 보여줘야 한다. 이걸 구현하려면 이미지가 에디터 조작 시점에 이미 서버에 올라가 있어야 한다. 게시글 저장 시점까지 이미지를 들고 있을 수가 없다.</p>
<p>그리고 이미지 업로드 실패와 게시글 저장 실패를 분리할 수 있다는 것도 장점이다. 이미지는 잘 올라갔는데 게시글 저장에서 실패했다면, 이미지를 다시 올릴 필요 없이 게시글만 재시도하면 된다.</p>
<hr>
<h2 id="확장-가능성">확장 가능성</h2>
<p>현재는 로컬 파일 서버에 저장하지만, <code>FileStorage</code> 인터페이스를 구현체만 교체하면 S3로 전환할 수 있다. <code>storageKey</code> 기반으로 설계한 이유가 여기 있다.</p>
<p>추후 고아 이미지(게시글에 참조되지 않는 이미지) 정리 배치, CDN 적용, XSS 필터링 강화 등도 자연스럽게 붙일 수 있는 구조다.</p>
<hr>
<pre><code>[Client]
   │
   ├─ POST /media/images (file)
   │        ↓
   │   LocalFileStorage
   │        ↓
   │   key + url 반환
   │
   └─ POST /posts (JSON)
            ↓
       PostService
            ↓
   HtmlImageExtractor
            ↓
      PostImage 저장</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[데이터베이스 보안]]></title>
            <link>https://velog.io/@siha_014/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EB%B3%B4%EC%95%88</link>
            <guid>https://velog.io/@siha_014/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EB%B3%B4%EC%95%88</guid>
            <pubDate>Thu, 29 Jan 2026 11:36:35 GMT</pubDate>
            <description><![CDATA[<h2 id="보안---사용자-권한-관리">보안 - 사용자 권한 관리</h2>
<p>데이터베이스 시스템에서는 다양한 사용자가 존재하며, 각 사용자에게 적절한 권한을 부여해야 보안을 유지할 수 있음</p>
<ul>
<li>GRANT: 사용자에게 권한 부여
GRANT 명령어를 사용하여 특정 사용자에게 데이터베이스에 대한 접근 권한을 부여할 수 있음
각 권한은 SEELCT, INSERT, UPDATE, DELETE등으로 세분화할 수 있음</li>
<li>REVOKE - 사용자 권한 회수
기존에 부여한 특정 권한을 회수할 때 사용
잘못된 권한을 부여했거나, 특정 사용자가 더 이상 해당 권한을 가질 필요가 없을 때 사용</li>
</ul>
<h2 id="보안---sql-인젝션-방어">보안 - SQL 인젝션 방어</h2>
<p>SQL 인젝션(SQL Injection): 공격자가 SQL문을 조작하여 데이터베이스를 무단으로 조작하는 해킹 방법</p>
<h3 id="prepared-statement준비된-쿼리를-사용한-sql-인젝션-방어">Prepared Statement(준비된 쿼리)를 사용한 SQL 인젝션 방어</h3>
<ul>
<li>미리 SQL문을 준비한 후, 사용자 입력값을 안전하게 바인딩하여 실행하는 방식</li>
<li>사용자가 입력한 값이 SQL 문에 직접 삽입되지 않으므로, SQL 인젝션 공격을 방어할 수 있음</li>
<li>SQL문을 미리 준비하고, 사용자 입력값을 ? 자리에 안전하게 바인딩</li>
<li>입력값이 자동으로 인코딩되므로 SQL 문법이 깨지지 않음 -&gt; 인젝션 공격 차단</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[인덱스 및 데이터 최적화]]></title>
            <link>https://velog.io/@siha_014/%EC%9D%B8%EB%8D%B1%EC%8A%A4-%EB%B0%8F-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%B5%9C%EC%A0%81%ED%99%94</link>
            <guid>https://velog.io/@siha_014/%EC%9D%B8%EB%8D%B1%EC%8A%A4-%EB%B0%8F-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%B5%9C%EC%A0%81%ED%99%94</guid>
            <pubDate>Wed, 28 Jan 2026 12:46:53 GMT</pubDate>
            <description><![CDATA[<h1 id="인덱스">인덱스</h1>
<p>데이터베이스에서 검색 속도를 향상시키기 위한 자료구조
책의 목차처럼, 원하는 데이터를 빠르게 찾을 수 있도록 도와줌
특정 컬럼에 인덱스를 생성하면 해당 컬럼을 검색할 때 속도가 크게 향상됨</p>
<h2 id="클러스터형-인덱스-vs-비클러스터형-인덱스">클러스터형 인덱스 vs 비클러스터형 인덱스</h2>
<p>기본적으로 모든 인덱스는 이 두 개념 중 하나에 속함
데이터 저장 방식과 검색 방식에 차이가 있음</p>
<h3 id="클러스터형-인덱스-clustered-index">클러스터형 인덱스 (Clustered Index)</h3>
<p><strong>데이터 자체가 인덱스 순서대로 저장됨</strong>
따라서 검색이 빠르지만, 데이터의 삽입/삭제 시 재정렬이 필요할 수 있음
PK에 자동으로 생성되며, 테이블당 하나만 존재 가능</p>
<h3 id="비클러스터형-인덱스-non-clustered-index">비클러스터형 인덱스 (Non-Clustered Index)</h3>
<p>PK 이외의 컬럼에서 검색 속도를 높이기 위해 사용됨
<strong>인덱스와 실제 데이터가 따로 저장</strong>되므로, 조회 시 한 단계 더 검색해야 함
인덱스 전용 테이블이 별도로 생성
한 테이블에 여러 개의 비클러스터형 인덱스를 생성할 수 있음
보조 인덱스(Secondary Index)라고도 불림</p>
<h2 id="인덱스의-자료구조">인덱스의 자료구조</h2>
<h3 id="b-tree구조">B-Tree구조</h3>
<p>모든 노드에 데이터가 저장됨
전체 데이터를 여러 블록(노드)로 분할해 저장
검색, 삽입, 삭제 속도 O(log N) -&gt; 빠르게 탐색 가능</p>
<h3 id="btree">B+Tree</h3>
<p>B-Tree의 단점을 보완한 자료구조
리프노드가 연결 리스트 형태로 구성되고, 리프 노드에만 데이터를 저장하여 범위 검색이 빠름
루트 노드, 중간 노드는 키 값만 저장</p>
<h4 id="클러스터형-인덱스의-자료구조btree를-사용하는-경우">클러스터형 인덱스의 자료구조(B+Tree를 사용하는 경우)</h4>
<p>클러스터형인덱스는 데이터 자체가 리프 노드에 저장됨
리프 노드에 실제 데이터가 저장되며, 키 값(id) 순서대로 정렬됨
즉, 인덱스를 따라가면서 데이터를 찾으면 추가적인 참조 없이 바로 데이터를 읽을 수 있음</p>
<h4 id="비클러스터형-인덱스의-자료구조btree를-사용하는-경우">비클러스터형 인덱스의 자료구조(B+Tree를 사용하는 경우)</h4>
<p>비클러스터형 인덱스는 인덱스와 실제 데이터가 별로도 저장됨
리프 노드에는 실제 데이터가 아닌 PK를 참조하는 값이 저장됨
검색을 수행하면 먼저 인덱스를 검색하고, 다시 PK를 참조하여 실제 데이터를 가져와야 함</p>
<h2 id="인덱스-생성-방법">인덱스 생성 방법</h2>
<h3 id="클러스터형-인덱스-생성">클러스터형 인덱스 생성</h3>
<p>테이블의 실제 데이터 저장 순서를 결정하는 인덱스
PK를 설정하면 자동으로 생성됨
테이블마다 하나만 생성 가능함
인덱스를 따라가면 바로 데이터에 접근할 수 있음(추가 탐색 불필요)</p>
<h3 id="기본-인덱스-생성비클러스터형-인덱스">기본 인덱스 생성(비클러스터형 인덱스)</h3>
<p>하나 또는 여러 컬럼에 대해 인덱스를 생성하는 방법
CREATE INDEX 구문으로 생성됨<br>비클러스터형 인덱스가 생성됨(이미 클러스터형 인덱스가 존재하므로)
인덱스를 따라가서 다시 테이블을 참조해야 데이터에 접근 가능함</p>
<h3 id="고유-인덱스unique-index-생성">고유 인덱스(Unique Index) 생성</h3>
<p>중복되지 않는 값을 저장해야 하는 컬럼에 사용
UNIQUE 제약 조건이 포함된 인덱스를 생성하면 중복값 입력이 불가능함
중복 방지를 위한 제약 조건이 주요 목적이며, 인덱스 자체는 보통 비클러스터형 인덱스로 생성됨
단, PK로 지정되는 경우에는 클러스터형 인덱스로 생성됨</p>
<h2 id="복합-인덱스-composite-index">복합 인덱스 (Composite Index)</h2>
<p>여러 개의 컬럼을 하나의 인덱스로 묶어서 생성하는 인덱스
하나의 인덱스로 여러 컬럼을 동시에 검색할 때 성능을 최적화함</p>
<p><strong>사용 이유</strong></p>
<ul>
<li>WHERE 조건에서 여러 개의 컬럼을 함께 검색하는 경우 속도를 최적화하기 위해</li>
<li>데이터베이스가 여러 개의 컬럼을 하나의 트리 구조에서 관리하도록 하여, 검색 효율을 높일 수 있음</li>
<li>하나의 인덱스를 사용하여여러 조건을 동시에 만족하는 데이터를 빠르게 찾을 수 있음</li>
</ul>
<p><strong>주의할 점</strong>
CREATE INDEX idx(col1, col2, col3)처럼 복합 인덱스를 만들면 인덱스를 활용하려면 col1부터 순서대로 WHERE 절에 포함되어야 함
예를 들어, col2, col3만 조건에서 사용하면 인덱스를 사용할 수 없음
즉, 인덱스는 지정한 컬럼 순서대로 조건이 걸려 있어야 성능이 최적화됨</p>
<h2 id="인덱스와-성능">인덱스와 성능</h2>
<p>인덱스는 데이터베이스에서 검색 성능을 향상시키지만, 잘못 사용하면 오히려 성능을 저하시킬 수도 있음
인덱스의 장점과 단점을 이해하고, 언제 인덱스를 활용해야 하는지 판단하는 것이 중요</p>
<p><strong>인덱스가 성능을 향상시키는 경우</strong>
데이터 조회(SELECT) 속도 향상
WHERE, JOIN, ORDER BY, GROUP BY 같은 연산이 포함된 쿼리 최적화
대량의 데이터를 검색할 때, 특정 컬럼을 기준으로 빠르게 탐색 가능</p>
<p><strong>인덱스가 성능을 저하시킬 수 있는 경우</strong>
크기가 작은 데이블에서 WHERE 검색(FULL SCAN이 빠를 수 있음)
INSERT, UPDATE, DELETE 성능 저하
(자주 변경되는 컬럼에 인덱스를 생성하면, 인덱스 유지 비용 증가)</p>
<h1 id="데이터-최적화">데이터 최적화</h1>
<p>데이터가 많아질수록 데이터베이스의 성능을 유지하기 위해 데이터를 분할하는 기법이 필요함</p>
<h2 id="샤딩sharding">샤딩(Sharding)</h2>
<p>데이터를 여러 개의 독립적인 데이터베이스(샤드)로 나누어 저장</p>
<p>샤딩이 필요한 경우</p>
<ul>
<li>데이터베이스가 너무 커서 하나의 DB서버에 저장할 수 없는 경우</li>
<li>전 세계 여러 지역에서 서비스를 운영하여 데이터 지역성을 최적화해야 하는 경우</li>
</ul>
<h2 id="파티셔닝partitioning">파티셔닝(Partitioning)</h2>
<p>하나의 데이터베이스 내에서 데이터를 여러 개의 파티션으로 분할
샤딩과 다르게 물리적으로 데이터베이스를 나누는 것이 아닌, 논리적으로 하나의 테이블을 여러 개의 파티션으로 나누는 방식
(수평 파티셔닝 / 수직 파티셔닝)</p>
<p>피티셔닝이 필요한 경우
특정 데이터가 너무 많아서 관리가 어려운 경우
특정 조건으로 자주 조회하는 데이터가 많아서, 검색 성능을 높이기 위해 데이터를 논리적으로 분리할 필요가 있는 경우</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[트랜잭션]]></title>
            <link>https://velog.io/@siha_014/%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98</link>
            <guid>https://velog.io/@siha_014/%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98</guid>
            <pubDate>Tue, 27 Jan 2026 12:26:33 GMT</pubDate>
            <description><![CDATA[<h1 id="트랜잭션">트랜잭션</h1>
<p>데이터베이스에서 하나의 논리적 작업 단위
모든 작업이 성공해야만 최종적으로 반영되고, 하나라도 실패하면 전체 작업이 취소(Rollback)됨
데이터의 일관성을 유지하기 위해 사용됨</p>
<h2 id="트랜잭션의-특성">트랜잭션의 특성</h2>
<ul>
<li><p>Atomicity (원자성)
: 트랜잭션 내의 모든 작업은 모두 성공 또는 모두 실패해야 함</p>
</li>
<li><p>Consistency (일관성)
: 트랜잭션이 실행되기 전과 후의 데이터가 일관성을 유지해야 함</p>
</li>
<li><p>Isolation (고립성)
: 여러 트랜잭션이 동시에 실행될 때, 서로 영향을 주지 않아야 함</p>
</li>
<li><p>Durability (지속성)
: 트랜잭션이 완료되면 그 결과가 영구적으로 반영되어야 함</p>
</li>
</ul>
<h2 id="트랜잭션-상태">트랜잭션 상태</h2>
<p>트랜잭션은 실행되는 동안 여러 상태를 거치며 진행</p>
<ul>
<li><p>Active (활성 상태)
: 트랜잭션이 시작되어 실행 중인 상태</p>
</li>
<li><p>Partilly Committed (부분 완료 상태)
: 트랜잭션이 모든 SQL문을 실행했지만 아직 확정(COMMIT)되지 않은 상태</p>
</li>
<li><p>Committed (완료 상태)
: 트랜잭션이 성공적으로 완료되어 데이터가 영구적으로 반영된 상태</p>
</li>
<li><p>Failed (실패 상태)
: 오류로 인해 트랜잭션이 정상적으로 완료되지 못한 상태</p>
</li>
<li><p>Aborted (중단 상태)
: 트랜잭션이 실패하여 ROLLBACK이 수행된 상태</p>
</li>
</ul>
<h2 id="트랜잭션의-복구">트랜잭션의 복구</h2>
<p>장애 발생 시 데이터 일관성을 보장하기 위한 복구 필요
트랜잭션의 원자성 보장을 위한 메커니즘</p>
<h3 id="지연-갱신-방식">지연 갱신 방식</h3>
<p>트랜잭션의 작업 내용을 로그에만 기록하고, 실제 DB 반영은 COMMIT 이후에 수행함
장애 발생 시, COMMIT된 트랜잭션만 반영하면 됨 -&gt; Redo 연산 사용
COMMIT되지 않은 트랜잭션은 무시하면 됨</p>
<blockquote>
<p><strong>즉시 갱신 방식</strong>
트랜잭션이 시작되고 쿼리를 하나하나 실행할 때마다 DB에 바로 반영하며, 변경 전 데이터는 Undo 로그에 함께 저장
장애 발생 시 트랜잭션 일부 작업이 반영되어 있을 수 있음
COMMIT되지 않은 트랜잭션 내의 작업을 되돌리기 위해 Undo 연산 사용
Undo 연산: 트랜잭션 도중 장애 발생 시, 이미 반영된 데이터를 원래 값으로 복구하는 작업</p>
</blockquote>
<h3 id="redo-연산">Redo 연산</h3>
<p>COMMIT된 트랜잭션이 장애로 인해 DB에 반영되지 못했을 경우, 트랜잭션 로그에 따라 다시 반영하는 작업
트랜잭션 로그에 기록된 값(New Value)을 DB에 반영함</p>
<h2 id="트랜잭션-격리-수준">트랜잭션 격리 수준</h2>
<p>하나의 트랜잭션에서 작업 중인 데이터가 다른 트랜잭션에 영향을 받지 않는 정도를 의미
반대로 하나의 트랜잭션에서 작업 중인 데이터를 다른 트랜잭션에서 어느 정도까지 접근 할 수 있는가를 나타냄</p>
<p><strong>트랜잭션 격리 수준의 설정</strong>
격리 수준을 낮게 설정 &gt;&gt; 동시성이 좋아짐
격리 수준을 높게 설정 &gt;&gt; 동시성은 떨어지나 정확성이 좋아짐
따라서 둘 사이의 trade-off를 고려해야 함</p>
<h3 id="1단계-read-uncommitted">1단계: READ UNCOMMITTED</h3>
<p>: 다른 트랜잭션이 CCOMMIT되지 않은 데이터도 읽을 수 있음
가장 낮은 격리 수준,
Dirty Read 발생 가능
(다른 트랜잭션이 COMMIT하지 않은 데이터를 읽을 수 있음) </p>
<h3 id="2단계-read-committed">2단계: READ COMMITTED</h3>
<p>: COMMIT된 데이터만 읽을 수 있음
Dirty Read를 방지하지만 Non-Repeatable Read 발생 가능
(같은 데이터를 다시 조회했을 때 값이 바뀌어 있음)</p>
<h3 id="3단계-repeatable-read">3단계: REPEATABLE READ</h3>
<p>: 같은 트랜잭션 내에서 동일한 데이터를 읽으면 항상 같은 값이 반환됨 (트랜잭션이 시작되기 전에 커밋된 내용에 대해서먄 조회)
Non-Repeatable Read를 방지하지만 Phantom Read 발생 가능
(같은 조건으로 조회했을 때 새로운 데이터가 나타남)
(REPEATABLE READ는 기존 행의 수정/삭제는 막지만, 삽입은 막지 못하기 때문)</p>
<h3 id="4단계-serializable">4단계: SERIALIZABLE</h3>
<p>: 트랜잭션을 직렬화하여 동작 (가장 엄격)
각 트랜잭션이 마치 하나씩 순서대로 실행된 것처럼 보이게 동작</p>
<h4 id="실무에서의-격리-수준-정도">실무에서의 격리 수준 정도</h4>
<p>일반적인 웹 애플리케이션: READ    COMMITTED / REPEATABLE READ
데이터 일관성이 중요한 경우 (EX. 은행 시스템): SERIALIZABLE을 사용하나 성능 저하를 고려해야 함
대량 트랜잭션이 필요한 경우 (EX. 쇼핑몰): READ COMMITTED으로 성능 우선
MySQL 기본값: REPEATABLE READ
PostgreSQL 기본값: READ COMMITTED
Oracle 기본값: READ COMMITTED</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[정규화]]></title>
            <link>https://velog.io/@siha_014/%EC%A0%95%EA%B7%9C%ED%99%94</link>
            <guid>https://velog.io/@siha_014/%EC%A0%95%EA%B7%9C%ED%99%94</guid>
            <pubDate>Mon, 26 Jan 2026 02:34:51 GMT</pubDate>
            <description><![CDATA[<h1 id="이상-현상">이상 현상</h1>
<p>정규화를 하지 않으면, 데이터 수정, 삽입, 삭제 시 문제가 발생할 수 있음</p>
<ol>
<li><p>삽입 이상 (Insertion Anomaly)
데이터를 삽입할 때 불필요한 데이터까지 입력해야 하는 문제</p>
</li>
<li><p>수정 이상 (Update Anomaly)
중복된 데이터가 존재할 경우, 하나만 수정하면 데이터 불일치가 발생하는 문제</p>
</li>
<li><p>삭제 이상 (Deletion Anomaly)
하나의 데이터를 삭제할 때, 연관된 다른 데이터까지 삭제되는 문제</p>
</li>
</ol>
<h1 id="정규화">정규화</h1>
<p>데이터베이스 설계에서 중복을 최소화하고 데이터 무결성을 유지하기 위한 과정
데이터를 여러 테이블로 나누어 <strong>이상 현상 발생을 방지*</strong>
데이터 변경 시 일관성을 유지하고, 불필요한 데이터 수정 비용을 줄임
관계형 데이터베이스에서 가장 중요한 설계 원칙 중 하나
정규화를 적용하면 저장 공간을 절약하고, 데이터 수정 시 오류를 방지할 수 있음</p>
<p>하지만 과도한 정규화는 JOIN 연산이 많아져 성능 저하를 유발할 수 있으므로, 적절한 수준에서 적용하는 것이 중요함</p>
<p><strong>반정규화</strong>
: 정규화를 적용한 데이터베이스에서 성능 최적화를 위해 일부 정규화를 되돌리는 과정
JOIN 연산이 많아져 조회 성능이 저하될 때 해결책으로 사용되며, 
데이터 중복을 일부 허용하여 읽기 속도를 향상시킨다.</p>
<h2 id="제1정규형-1nf">제1정규형 (1NF)</h2>
<p>각 컬럼이 하나의 원자값(하나의 값)만 가져야 한다는 규칙을 의미</p>
<h2 id="제2정규형-2nf">제2정규형 (2NF)</h2>
<p>부분 함수 종속 제거
1NF를 만족하고 ,기본 키의 일부만으로 특정 컬럼이 결정되는 경우 제거
즉, 기본 키의 일부에만 종속된 속성을 분리해야 함</p>
<h2 id="제3정규형-3nf">제3정규형 (3NF)</h2>
<p>제2정규형을 만족하면서, 이행적 종속을 제거하는 과정
기본 키가 아닌 속성이 다른 일반 속성에 종속되는 경우를 제거해야 함
즉, 기본 키가 아닌 컬럼은 오직 기본 키에만 의존해야 함</p>
<h2 id="bcnf-boycecodd-normal-form">BCNF (Boyce/Codd Normal Form)</h2>
<p>보다 일반적인 정규형을 제안
릴레이션 R의 결정자 모두가 후보 키이면 릴레이션 R은 BCNF이다.
강한 제3규형</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[데이터베이스 개요]]></title>
            <link>https://velog.io/@siha_014/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EA%B0%9C%EC%9A%94</link>
            <guid>https://velog.io/@siha_014/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EA%B0%9C%EC%9A%94</guid>
            <pubDate>Sat, 24 Jan 2026 12:03:36 GMT</pubDate>
            <description><![CDATA[<h1 id="데이터베이스">데이터베이스</h1>
<p>체계적으로 정리된 데이터의 집합으로, 여러 사용자가 데이터를 효율적으로 저장, 검새, 수정, 삭제할 수 있도록 관리하는 시스템
일반적으로 데이터베이스는 데이터베이스 관리 시스템(DBMS)을 통해 데이터를 운영하며, 데이터의 무결성과 일관성을 유지하도록 설계됨</p>
<p><strong>데이터 VS 정보</strong>
데이터: 가공되지 않은 원시 값
정보: 데이터를 가공하여 의미를 부여한 것</p>
<h2 id="특성">특성</h2>
<ul>
<li>실시간 접근성
언제든지 데이터를 저장하고 조회할 수 있음</li>
<li>동시성 (Concurrency)
여러 사용자가 동시에 같은 데이터를 읽고 쓸 수 있음</li>
<li>일관성 (Consistency)
데이터가 항상 올바른 상태를 유지해야 함</li>
<li>무결성 (Integrity)
데이터가 규칙에 맞게 저장되도록 보장</li>
<li>데이터 중복 최소화
데이터를 효율적으로 저장하고 중복을 방지</li>
<li>보안성
허가된 사용자만 데이터에 접근 가능</li>
<li>독립성
데이터와 프로그램이 분리되어 있음 -&gt; 데이터를 변경해도 프로그램 수정 없이 사용 가능</li>
</ul>
<h2 id="dbms">DBMS</h2>
<p>Database Management System
데이터베이스를 효과적으로 관리하기 위해 필요
데이터를 저장, 수정, 검색, 삭제하는 기능을 제공하는 소프트웨어
MySQL, 오라클 등</p>
<p><strong>기능</strong></p>
<ul>
<li>데이터 저장 및 관리 -&gt; 데이터를 구조화된 형태로 저장</li>
<li>데이터 검색 및 수정 -&gt; SQL을 사용해 데이터를 빠르게 검색하고 변경 가능</li>
<li>동시성 제어 -&gt; 여러 사용자가 같은 데이터를 동시에 사용 가능</li>
<li>보안 관리 -&gt; 사용자 권한을 설정하여 데이터를 접근을 제한</li>
<li>백업 및 복구 -&gt; 장애 발생 시 데이터 손실 없이 복구 가능</li>
</ul>
<p><strong>언어 4종류</strong></p>
<ul>
<li><p>DML (데이터 조작어)
SELECT, INSERT, UPDATE, DELETE
데이터를 검색하고 추가, 수정, 삭제하는 명령어
자주 사용하는 SQL</p>
</li>
<li><p>DDL (데이터 정의어)
CREATE, ALTER, DROP
테이블, 인덱스 등을 생성하거나 수정할 때 사용
초기 DB 설계 시 많이 사용되며, 운영 중에는 가끔 사용됨</p>
</li>
<li><p>DCL (데이터 제어어)
GRANT, REVOKE
DB 관리자(DBA)가 보안 관리를 위해 사용
일반 개발자는 사용할 일이 거의 없음</p>
</li>
<li><p>TCL (트랜재션 제어어)
COMMIT, ROLLBACK, SAVEPOINT
여러 개의 데이터 변경 작업을 하나의 트랜잭션으로 묶을 때 사용
특히 금융, 쇼핑몰 결제 시스템 등에서는 필수</p>
</li>
</ul>
<h3 id="rdbms">RDBMS</h3>
<p>관계형 데이터베이스 관리 시스템(Relational DBMS)
데이터를 테이블 형태로 저장하고, 테이블 간의 관계를 이용하여 데이터를 관리하는 데이터베이스 시스템
SQL 사용 -&gt; 데이터를 조회, 추가, 수정, 삭제
관계 기반 -&gt; 테이블 간 연결을 통해 중복 최소화
MySQL, PostgreSQL, Oracle, SQL Server 등이 대표적</p>
<p>테이블의 관계는 일대일(1:1), 일대다(1:N), 다대일(N:1), 다대다(N:M)이 있다.</p>
<h2 id="데이터베이스-모델링">데이터베이스 모델링</h2>
<p>실제 DB를 만들기 전에 구조를 설계하는 과정으로, 3단곌 진행</p>
<p>1단계 - <strong>개념적 모델링</strong>
: 무엇을 저장할지 정리하는 단계
데이터 구조를 간단한 개념으로 표현 &gt;&gt; 개체(Entity), 관계(Relationship)
ERD(Entity-Relationship diagram)가 많이 사용됨</p>
<p>2단계 - <strong>논리적 모델링</strong>
: 개념적 모델링을 기반으로 릴레이션 스키마 형태로 구체화
테이블, 속성(컬럼), 관계(PK, FK) 설정
데이터 정규화 적용</p>
<p>3단계 - <strong>물리적 모델링</strong>
논리적 모델을 실제 데이터베이스에 적용하는 단계
DBMS에 맞는 데이터 타입, 인덱스, 제약 조건 설정</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[네트워크 보안과 실무]]></title>
            <link>https://velog.io/@siha_014/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EB%B3%B4%EC%95%88%EA%B3%BC-%EC%8B%A4%EB%AC%B4</link>
            <guid>https://velog.io/@siha_014/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EB%B3%B4%EC%95%88%EA%B3%BC-%EC%8B%A4%EB%AC%B4</guid>
            <pubDate>Fri, 23 Jan 2026 13:14:35 GMT</pubDate>
            <description><![CDATA[<h1 id="네트워크-보안">네트워크 보안</h1>
<p><strong>네트워크 보안이 필요한 이유</strong></p>
<ul>
<li>개인 정보 보호
사용자 계정, 신용카드 정보 등 중요한 데이터가 보호되지 않으면 유출될 수 있음</li>
<li>데이터 변조 방지
전송 중 데이터가 변조되지 않도록 보호해야 함</li>
<li>시스템 다운 방지
DDos 공격 등으로 네트워크가 마비되지 않도록 보안이 필요</li>
<li>악성 코드 및 해킹 방지
랜섬웨어, 바이러스 등의 공격으로부터 시스템을 보호해야 함</li>
</ul>
<h2 id="네트워크-보안의-3대-요소">네트워크 보안의 3대 요소</h2>
<ol>
<li><p><strong>기밀성</strong> (Confidentiality) - 정보가 보호되어야 함
허가된 사용자만 데이터에 저븐할 수 있도록 보호
암호화를 통해 데이터를 안전하게 유지
예시: 로그인 시스템(비밀번호 보호), HTTPS(암호화된 웹 통신)</p>
</li>
<li><p><strong>무결성</strong> (Integrity) - 데이터가 변조되지 않아야 함
데이터를 전송하는 동안 변경되거나 조작되지 않도록 보호
예시: 디지털 서명, 체크섬을 이용한 데이터 무결성 검증</p>
</li>
<li><p><strong>가용성</strong> (Availability) - 네트워크가 항상 작동해야 함
시스템이 장애 없이 정상적으로 운영될 수 있도록 보호
DDos 공격(분산 서비스 거부 공격) 방어 필요
예시: 방화벽을 이용한 네트워크 보호, 서버 이중화(백업 서버 운영)</p>
</li>
</ol>
<h2 id="대칭키와-비대칭키">대칭키와 비대칭키</h2>
<h3 id="대칭키">대칭키</h3>
<p><strong>대칭키 암호화 (Symmetric Key Encryption)</strong>
<strong>하나의 비밀키를 사용</strong>하여 데이터를 암호화하고 복호화
송신자와 수신자가 같은 키를 공유해야 함
속도가 빠르고 연산이 효율적이지만, 키 분배가 어려움</p>
<p>대칭키 암호화 과정:
A가 데이터를 암호화할 때 비밀키(K)를 사용하여 암호화
암호화된 데이터를 B에게 전송
B가 데이터를 복호화할 때 같은 비밀키(K)를 사용하여 보호화</p>
<p>장점:</p>
<ul>
<li>빠른 속도</li>
<li>연산이 간단하여 대량의 데이터 처리에 적합</li>
</ul>
<p>단점</p>
<ul>
<li>키 분배가 어려움 (중간에 키가 유출되면 보안 취약)</li>
</ul>
<h3 id="비대칭키">비대칭키</h3>
<p><strong>비대칭키 암호화 (Asymmetric Key Encryption)</strong>
<strong>공개키(Public Key)</strong>와 <strong>비밀키(Private Key)</strong> 두 개의 키를 사용
공개키는 누구나 알 수 있고, 비밀키는 본인만 소유
암호화와 복호화에 각각 다른 키를 사용하므로 보안성이 높음</p>
<p>비대칭키 암호화 과정:
A가 B에게 보낼 데이터를 B의 공개키로 암호화
암호화된 데이터를 전송
B는 자신의 비밀키로 데이터를 보호화</p>
<p>용도:</p>
<ul>
<li><p>암호화 용도 (기밀성):
공개키로 암호화 -&gt; 비밀키로 복호화</p>
</li>
<li><p>전자 서명 용도 -&gt; 공개키로 복호화</p>
</li>
</ul>
<p>장점</p>
<ul>
<li>키 분배가 쉽고 보안성이 높음</li>
<li>전자 서명, 인증서 등에 활용 가능</li>
</ul>
<p>단점</p>
<ul>
<li>연산 속도가 느림</li>
<li>대량의 데이터를 처리하기에는 부담이 큼</li>
</ul>
<h2 id="https-ssltls">HTTPS, SSL/TLS</h2>
<p>HTTPS = HTTP + 보안(SSL/TLS)
SSL/TLS는 네트워크에서 데이터를 암호화하는 보안 프로토콜
HTTPS는 HTTP의 보안 강화 버전 (HTTP:80번 포트 / HTTPS: 443번 포트)
데이터를 암호화하여 안전한 웹 통신을 제공하는 프로토콜
웹사이트에서 로그인, 결제 등 민감한 데이터를 보호하기 위해 사용</p>
<p>SSL(Secure Sockets Layer): 초기 보안 프로토콜
TLS(Trasport Layer Security): SSL의 개선 버전, 현재 HTTPS에서 사용됨</p>
<p>SSL/TLS의 주요 기능</p>
<ul>
<li>암호화(Encryption): 데이터가 네트워크에서 노출되지 않도록 암호화</li>
<li>인증(Authentication): 웹사이트가 신뢰할 수 있는 사이트인지 인증</li>
<li>데이터 무결성(Integrity): 데이턱가 전송 중 변경되지 않도록 보호</li>
</ul>
<h2 id="방화벽">방화벽</h2>
<p>네트워크에서 허용된 트래픽만 통과시키고, 불필요하거나 위험한 트래픽을 차단하는 역할
개업, 기관, 개인 네트워킹에서 해킹, 악성 코드, 불법 접근 등을 방지하기 위해 사용</p>
<p>주요 기능</p>
<ul>
<li>IP 주소, 포트 번호, 프로토콜 기반으로 트래픽 필터링</li>
<li>허용된 네트워크 요청만 통과, 비인가된 요청은 차단</li>
<li>DDoS 공격, 바이러스, 악성 코드 차단 기능 제공 가능</li>
</ul>
<p>필터링 과정</p>
<ol>
<li>클라이언트(외부)가 서버에 접속 요청 (예: 웹사이트 접속)</li>
<li>방화벽이 패킷을 검사
출발지 IP, 목적지 IP, 포트 번호, 프로토콜을 확인</li>
<li>방화벼의 보안 규칙에 따라 결정
허용된 트래픽은 내부 네트워크로 전달/ 차단된 트래픽은 즉시 폐기</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[데이터링크 계층]]></title>
            <link>https://velog.io/@siha_014/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A7%81%ED%81%AC-%EA%B3%84%EC%B8%B5</link>
            <guid>https://velog.io/@siha_014/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A7%81%ED%81%AC-%EA%B3%84%EC%B8%B5</guid>
            <pubDate>Wed, 21 Jan 2026 06:17:23 GMT</pubDate>
            <description><![CDATA[<h1 id="데이터링크-계층">데이터링크 계층</h1>
<p>같은 네트워크 내에서 신뢰성 있는 데이터 전송을 담당
즉, 같은 LAN 내에서 장치 간 데이터를 주고 받는 역할을 함</p>
<p><strong>데이터링크 계층의 역할</strong></p>
<ul>
<li>네트워크 계층에서 받은 데이터를 프레임(frame)단위로 변환하여 물리 계층으로 전송</li>
<li>같은 네트워크 (LAN) 내에서 오류 없이 데이터를 전달</li>
<li>MAC 주소를 기반으로 목적지 장치를 식별하여 데이터 전송</li>
<li>충돌을 방지하고 효율적인 데이터를 전달하는 MAC(Media Access Control) 기능 제공</li>
</ul>
<p>데이터링크 계층의 주요 프로토콜: Ethernet(이더넷), Wi-Fi(무선 LAN)</p>
<h2 id="이더넷-ethernet">이더넷 (Ethernet)</h2>
<p>유선 네트워크(LAN)에서 데이터를 전송하는 가장 널리 사용되는 기술
즉, 같은 네트워크 내에서 장치 간 데이터를 MAC 주소를 기반으로 전달</p>
<p>특징</p>
<ul>
<li>MAC 주소를 사용하여 장치 간 데이터 전송</li>
<li>프레임 단위로 데이터를 전송</li>
<li>CSMA/CD(충돌 감지) 방식 사용</li>
<li>스위치를 사용하여 네트워크를 효율적으로 관리</li>
</ul>
<p>CSMA/CD
네트워크에서 충돌을 방지하는 방식
CS: 데이터를 보내기 전에 네트워크가 사용 중인지 확인 (Carrier Sense)
MA: 여러 장치가 네트워크를 공유하며 데이터를 전송 (Multiple Access)
CD: 충돌이 발생하면 잠시 기다린 후 데이터를 재전송 (Collision Detection)
-&gt; 하지만 현재는 스위치를 사용하여 충돌이 거의 발생하지 않음</p>
<h2 id="스위치-switch">스위치 (Switch)</h2>
<p>네트워크에서 장치 간 데이터를 효율적으로 전달하는 장비
MAC 주소를 기반으로 프레임을 전달하며, 허브와 다르게 충돌 없이 통신 가능 (허브 대체)
네트워크 트래픽을 최적화하고 충돌을 방지
현대 이더넷 네트워크에서 필수적인 장비로, 대부분의 네트워크에서 사용됨</p>
<table>
<thead>
<tr>
<th>구분</th>
<th>스위치(Switch)</th>
<th>허브(Hub)</th>
</tr>
</thead>
<tbody><tr>
<td>데이터 전달 방식</td>
<td>MAC 주소 확인 후 해당 포트로만 전달</td>
<td>모든 포트로 브로드캐스트</td>
</tr>
<tr>
<td>충돌 발생 여부</td>
<td>없음</td>
<td>있음</td>
</tr>
<tr>
<td>네트워크 효율성</td>
<td>트래픽을 최적화하여 속도 빠름</td>
<td>불필요한 트래픽 증가로 속도 저하</td>
</tr>
<tr>
<td>사용 여부</td>
<td>현재 대부분의 네트워크에서 사용</td>
<td>거의 사용되지 않음</td>
</tr>
</tbody></table>
<p><strong>전송 과정</strong></p>
<ol>
<li>MAC  주소 학습
장치가 네트워크에 연결되면, 스위치는 해당 포트의 MAC 주소를 기억 (MAC 주소 테이블 생성)</li>
<li>프레임 수신 및 전달
프레임을 받으면 목적지 MAC 주소를 확인
MAC 주소 테이블에 해당 MAC 주소가 있으면 해당 포트로만 데이터 전달
만약 MAC 주소를 모르면 브로드캐스트(네트워크 전체 전송) 후 학습</li>
</ol>
<h2 id="mac-주소">MAC 주소</h2>
<p>네트워크 장치(컴퓨터, 스마트폰, 라우터 등)에 부여된 고유한 식별 주소
네트워크에서 데이터를 올바른 장치로 전달하기 위해 사용됨
하드웨어에 내장된 주소이므로 변경 불가능</p>
<p>구조: 
48비트(6바이트), 16진수 6쌍(12자리)
24비트는 제조회사 번호, 뒤의 24비트는 장치별 고유 식별자</p>
<p><strong>MAC 주소가 사용되는 상황</strong></p>
<ul>
<li>이더넷 통신 -&gt; 네트워크 내에서 데이터 프레임 전송</li>
<li>Wi-Fi 연결 -&gt; 무선 네트워크에서 장치 식별</li>
<li>네트워크 보안 (MAC 주소 필터링) -&gt; 특정 장치만 네트워크에 접근 허용</li>
<li>ARP -&gt; IP 주소를 MAC 주소로 변환</li>
</ul>
<h2 id="arp-address-resolution-protocol">ARP (Address Resolution Protocol)</h2>
<p>IP 주소를 MAC 주소로 변환하는 프로토콜
같은 네트워크(LAN) 내에서 장치 간 통신을 가능하게 함</p>
<p>필요한 이유</p>
<ul>
<li><p>네트워크에서 데이터를 전송할 때 IP 주소만으로는 장치를 직접 찾을 수 없음</p>
</li>
<li><p>이더넷, Wi-Fi같은 데이터링크 게층에서는 MAC 주소를 기반으로 통신</p>
</li>
<li><p>따라서, IP 주소 -&gt; MAC 주소변환 과정 (ARP)가 필요함</p>
</li>
</ul>
<p><strong>동작 과정</strong></p>
<ul>
<li>APR 요청
출발지 장치가 목적지 MAC 주소를 모르므로, 네트워크 전체에 브로드캐스트 전송</li>
<li>ARP 응답
목적지 장치가 MAC 주소 응답</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[네트워크 계층]]></title>
            <link>https://velog.io/@siha_014/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EA%B3%84%EC%B8%B5</link>
            <guid>https://velog.io/@siha_014/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EA%B3%84%EC%B8%B5</guid>
            <pubDate>Tue, 20 Jan 2026 04:00:13 GMT</pubDate>
            <description><![CDATA[<h1 id="네트워크-계층">네트워크 계층</h1>
<p>데이터를 네트워크를 통해 목적지까지 전달하는 역할을 수행
라우팅을 통해 최적으리 경로를 선택하고, IP 주소를 기반으로 패킷을 전달하는 것이 핵심 기능</p>
<p><strong>라우터(Router)</strong>
라우터는 네트워크 간에 데이터 패킷을 전달하고 최적의 경로를 선택하는 역할을 수행
라우터는 IP 주소를 기반으로 패킷을 목적지까지 전달하며, 여러 네트워크를 연결하는 핵심 장치</p>
<p>라우터 주요  기능</p>
<ul>
<li><p>네트워크 간 패킷 전달 (Routing)
서로 다른 네트워크 간에 데이터 패킷을 전달
라우터는 패킷의 목적지 IP 주소를 확인하고, 해당 패킷을 다음 네트워크로 전달</p>
</li>
<li><p>최적 경로 선택
패킷이 목적지까지 도달하기 위해 가장 효율적인 경로를 선택</p>
</li>
<li><p><em>라우팅 테이블*</em>을 이용하여 목적지 네트워크에 대한 정보를 관리하고, 최적 경로를 결정</p>
</li>
</ul>
<h2 id="ip-internet-protocol">IP (Internet Protocol)</h2>
<p>데이터를 목적지까지 전달하는 핵심 프로토콜
패킷을 전송하는 역할을 하지만, 신뢰성을 보장하지 않음 -&gt; TCP, UDP와 함께 사용되어 데이터 전송을 보장</p>
<h4 id="ipv4">IPv4</h4>
<ul>
<li>32비트 주소 체계</li>
<li>점(.)으로 구분된 4개의 10진수</li>
<li>패킷 헤더 크기: 가변적</li>
<li>보안 기능 없음</li>
</ul>
<h4 id="ipv6">IPv6</h4>
<ul>
<li>128비트 주소 체계</li>
<li>콜론(:)으로 구분된 8개의 16진수</li>
<li>패킷 헤더 크기: 고정적 (단순화됨)</li>
<li>보안 강화 (IPSec 기본 지원)</li>
</ul>
<h3 id="ip의-계층화">IP의 계층화</h3>
<p>IP 주소는 계층적인 구조를 가지고 있으며, 네트워크를 효율적으로 관리하고 라우팅을 최적화하기 위해 설계됨
IP 주소 계층화를 통해 대규모 네트워크를 작은 네트워크로 나누고, 라우팅을 간소화하며, 주소 낭비를 최소화할 수 있음</p>
<h4 id="ip-주소의-계층적-구조">IP 주소의 계층적 구조</h4>
<ul>
<li>IP 주소는 네트워크 부분(network)과 호스트 부분(host)으로 나뉨
네트워크 부분: 해당 IP가 속한 네트워크를 식별
호스트 부분 : 네트워크 내 개별 장치를 식별</li>
<li>서브넷 마스크
IP 주소에서 네트워크 부분과 호스트 부분을 구분하는 값
32비트 값으로 표현
네트워크 부분의 개수를 사용하여 /20, /24 등의 형식으로도 표현됨
서브넷: 동일 네트워크 부분에 속한 디바이스 집합
(라우터는 여러 서브넷에 속해서 여러개의 IP를 가질 수 있음)</li>
</ul>
<h2 id="nat-network-address-translation">NAT (Network Address Translation)</h2>
<p>: 네트워크 주소 변환
사설 IP를 공인 IP로 변환하여 인터넷과 통신할 수 있도록 하는 기술</p>
<h4 id="nat의-필요성">NAT의 필요성</h4>
<ul>
<li><p>IPv4 주소 부족 문제 해결
공인 IP 주소는 한정적이므로 모든 기기에 공인 IP를 할당할 수 없음
NAT를 사용하면 하나의 공인 IP를 여러 기기가 공유할 수 있음</p>
</li>
<li><p>보안 강화
내부 네트워크(사설 IP)가 직접 인터넷에 노출되지 않음
외부에서 직접 내부 네트워크 접근이 불가능하여 보안이 강화됨</p>
</li>
</ul>
<h4 id="기본-동작-원리">기본 동작 원리</h4>
<ul>
<li>내부 네트워크(사설 IP) -&gt; 공인 IP로 변환 -&gt; 인터넷 통신 가능</li>
<li>반대로 인터넷에서 받은 응답을 공인 IP에서 내부 네트워크(사설 IP)로 변환하여 전달</li>
</ul>
<h2 id="dhcp-dynamic-host-configuration-protocol">DHCP (Dynamic Host Configuration Protocol)</h2>
<p>네트워크에서 장치에 IP 주소를 자동으로 할당하는 프로토콜</p>
<p>필요한 이유: 
네트워크에 연결된 모든 장치는 IP 주소가 필요함
수동으로 IP를 설정하면 충돌 문제, 관리 어려움, 변경 필요 등의 불편함 발생</p>
<p>역할</p>
<ul>
<li>사용자 개입 없이 장치가 IP를 받을 수 있음</li>
<li>네트워크 설정 자동화
-&gt; 서브넷 마스크, 게이트웨이, DNS 서버 정보도 자동 제공</li>
</ul>
<p>동작 과정</p>
<ol>
<li><p>DHCP DISCOVER (검색)
클라이언트 -&gt; 브로트캐스트로 DHCP 서버를 찾음</p>
</li>
<li><p>DHCP OFFER (제안)
DHCP 서버 -&gt; 클라이언트에게 사용 가능한 IP 주소를 제안</p>
</li>
<li><p>DHCP REQUEST (요청)
클라이언트 DHCP 서버에 특정 IP 주소를 요청</p>
</li>
<li><p>DHCP ACK (승인)
DHCP 서버 -&gt; 클라이언트의 요청을 승인하고 IP를 할당</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[전송 계층]]></title>
            <link>https://velog.io/@siha_014/%EC%A0%84%EC%86%A1-%EA%B3%84%EC%B8%B5</link>
            <guid>https://velog.io/@siha_014/%EC%A0%84%EC%86%A1-%EA%B3%84%EC%B8%B5</guid>
            <pubDate>Mon, 19 Jan 2026 13:30:06 GMT</pubDate>
            <description><![CDATA[<h1 id="전송-계층">전송 계층</h1>
<p>네트워크 통신에서 송신지와 수신자 간의 데이터 전송을 관리하고, <strong>신뢰성</strong>과 정확성을 보장하는 역할 수행
TCP/IP 4계층 모델과 OSI 7계층 모델에서 공통적으로 정의되며, 데이터가 손실 없이 정확히 전달되도록 다양한 메커니즘을 제공함</p>
<h2 id="tcp--udp">TCP &amp; UDP</h2>
<h3 id="tcp-transmission-control-protocol">TCP (Transmission Control Protocol)</h3>
<p>신뢰성 있는 연결, 데이터 순서 보장, 흐름/오류 제어
웹 브라우징(HTTP), 파일 전송 (FTP)</p>
<h3 id="udp-user-datagram-protocol">UDP (User Datagram Protocol)</h3>
<p>비연결형, 신뢰성 낮음, 빠른 데이터 전송
동영상 스트리밍, 온라인 게임</p>
<table>
<thead>
<tr>
<th>특징</th>
<th>TCP</th>
<th>UDP</th>
</tr>
</thead>
<tbody><tr>
<td>연결 방식</td>
<td><strong>연결 지향적</strong>: 3-way handshake</td>
<td><strong>비연결 지향적</strong>: 연결 설정 없이 데이터 전송</td>
</tr>
<tr>
<td>신뢰성</td>
<td>데이터 손실, 중복, 순서 오류 방지</td>
<td>신뢰성 보장하지 않음</td>
</tr>
<tr>
<td>속도</td>
<td>느림(연결 설정, 오류 제어로 인한 오버헤드)</td>
<td>빠름(단순 데이터 전송)</td>
</tr>
<tr>
<td>오류 제어</td>
<td>데이터 전송 중 오류 감지 및 복구</td>
<td>오류 제어 없음</td>
</tr>
<tr>
<td>순서 보장</td>
<td>데이터 순서 보장 (시퀀스 번호 사용)</td>
<td>보장 X</td>
</tr>
<tr>
<td>헤더 크기</td>
<td>20~60바이트 (복잡한 구조)</td>
<td>8바이트 (간단한 구조)</td>
</tr>
<tr>
<td>흐름 제어</td>
<td>수신자의 데이터 처리 속도에 맞게 흐름 제어</td>
<td>흐름 제어 없음</td>
</tr>
<tr>
<td>사용 사례</td>
<td>신뢰성이 중요한 애플리케이션</td>
<td>속도가 중요한 애플리케이션</td>
</tr>
</tbody></table>
<h3 id="tcp-커넥션-생성">TCP 커넥션 생성</h3>
<h4 id="3-way-handshake">3-Way Handshake</h4>
<p>TCP 프로토콜에서 송신자와 수신자 간 신뢰성 있는 연결을 설정하기 위한 과정을 말함
데이터 전송 전에 양측이 통신 준비가 되었는지 확인하고, 초기 연결 상태를 설정함으로써 데이터 전송의 신뢰성을 보장함</p>
<p><strong>3-Way Handshake 과정</strong></p>
<ul>
<li><p>SYN (Synchronize)
송신자(클라이언트)는 연결을 요청하기 위해 수신자(서버)에게 SYN 패킷을 전송
내용: 클라이언트의 초기 시퀀스 번호</p>
</li>
<li><p>SYN-ACK (Synchronize-Acknowledge)
서버는 연결 요청을 받고, 클라이언트의 요청을 승인하는 ACK와 함께 자신의 연결 요청인 SYN을 포함하여 응답
내용: 클라이언트의 시퀀스 번호에 대한 ACK + 서버의 초기 시퀀스 번호</p>
</li>
<li><p>ACK (Acknowledge)
클라이언트는 서버의 응답을 확인한 뒤, 최종적으로 ACK 패킷을 보내 연결이 완료되었음을 알림
내용: 서버의 시퀀스 번호에 대한 ACK</p>
</li>
<li><p>이후 송신자, 수신자 사이에 커넥션 생성</p>
</li>
</ul>
<h3 id="tcp-커넥션-해제">TCP 커넥션 해제</h3>
<h4 id="4-way-handshake">4-Way Handshake</h4>
<p>TCP 프로토콜에서 송신자와 수신자 간의 연결을 종료하기 위한 과정
데이터 전송이 완료된 후, 양측이 연결을 종료하기 위해 FIN(종료) 패킷을 교환하며, 네트워크 자원을 해제하고 연결을 정리함</p>
<p><strong>4-Way Handshake 과정</strong></p>
<ul>
<li><p>FIN (Connection Termination Request)
송신자가 연결 종료를 요청하며 FIN 패킷을 보냄</p>
</li>
<li><p>ACK(Acknowledge for FIN)
수신자가 FIN 패킷을 받고 이를 확인했다는 ACK 패킷을 송신자에게 보냄</p>
</li>
<li><p>FIN (Receiver&#39;s Termination Request)
수신자도 연결 종료를 요청하며 FIN 패킷을 송신자에게 보냄</p>
</li>
<li><p>ACK (Acknowledge for Receiver&#39;s FIN)
송신자가 수신자의 FIN 패킷을 받고, 종료를 확인하는 ACK 패킷을 수신자에게 보냄</p>
</li>
<li><p>이후 연결이 완전히 종료됨</p>
</li>
</ul>
<h2 id="rdt-reliable-data-transfer">RDT (Reliable Data Transfer)</h2>
<p>신뢰성 있는 데이터 전송을 위한 프로토콜을 설명하는 개념적인 모델,
데이터를 송신 측에서 수신 측으로 오류, 손실 없이 순서대로 전달하기 위해 설계</p>
<ul>
<li><p>RDT 1.0
: 전송되는 모든 데이터가 유실 없고 에러도 없는 이상적인 상황
데이터 전송 중 오류가 발생하지 않는 완벽한 환경에서 동작
송신자는 데이터를 전송하고, 수신자는 이를 문제없이 받음
문제점: 오류 검출, 복구, ACK 등이 필요 없음, 이상적인 환경에만 적용 가능</p>
</li>
<li><p>RDT 2.0
: 전송되는 데이터가 유실은 없으나 에러가 생길 수 있음
에러가 발생할 수 있는 환경에서 동작
데이터 손상 여부를 감지하기 위해 체크섬과 같은 오류 검출 메커니즘 사용
수신 측은 데이터를 수신한 후:
ACK: 데이터가 제대로 수신되었음을 확인
NAK: 데이터가 손상되었음을 송신 측에 알림 -&gt; 송신 측은 데이터를 재전송
문제점: ACK/NAK 패킷이 손실되거나 손상되면 문제가 발생할 수 있음</p>
</li>
<li><p>RDT 2.1
: 만약 리시버가 보내는 ACK, NAK에 에러가 생긴다면?
sender가 응답으로 에러 패킷을 받으면 이게 ACK인지 NAK인지 구별 불가능
그래서 sender는 무조건 보낸 데이터를 재전송함
문제는 receiver가 새 데이터인지 중복 데이터인지 구별 불가
구별을 위해 데이터 패킷의 헤더에 시퀀스 번호 추가
이제 receiver는 시퀀스 번호를 통해 중복 데이터를 감지하고 처리 (시퀀스 넘버 확인후 순서 맞으면 받고 안 맞으면 버림)</p>
</li>
<li><p>RDT 2.2
: NAK 없애고 ACK만으로 동작하게
시퀀스 번호가 추가되니 NAK가 불필요
MAK를 제거하고, ACK만을 사용하여 신뢰성 보장
receiver는 에러 데이터의 경우 ACK를 보내되 같은 시퀀스 번호로 전송
sender는 시퀀스 번호를 보고 다음 데이터를 보낼지 동일 데이터를 다시 보낼지 결정</p>
</li>
<li><p><em>즉, 시퀀스 번호만으로 NAK, ACK를 판단*</em></p>
</li>
<li><p>RDT 3.0
: 전송되는 데이터가 에러, 유실 둘 다 발생할 수 있음
데이터 유실이 발생하는 경우를 대비하여 sender는 receiver로부터 일정 시간 응답을 받지 못하면 데이터를 재전송
그래서 sender는 타이머를 사용하게 됨
타이머의 시간을 잘 설정하는 것이 중요
시간이 짧으면 유실이 발생하지 않았는데도 유실로 판단할 수 있고, 길면 유실 발생 시 복구가 느림</p>
</li>
</ul>
<h3 id="gbn-selective-repeat">GBN, Selective Repeat</h3>
<h4 id="gbn-go-back-n">GBN (Go-Back-N)</h4>
<p>RDT 3.0의 구현 방식 중 하나
sender는 N개의 데이터 패킷(슬라이딩 윈도우)을 동시에 전송할 수 있음
하지만, 수신 측에서 오류가 발생한 패킷 이후의 모든 패킷을 무효화하고 해당 패킷부터 다시 전송함
<strong>특징</strong></p>
<ul>
<li>단순함: 손실된 패킷 이후의 모든 데이터를 다시 전송하기 때문에 구현이 간단함</li>
<li>낭비: 손실된 하나의 패킷 때문에 이후의 모든 패킷이 다시 전송되므로 네트워크 대역폭 낭비될 수 있음</li>
</ul>
<p><strong>동작 방식</strong>
sender는 N개의 패킷을 전송하고, 각 패킷에 대해 ACK를 기다림
ACK가 도착하지 않으면, 타임아웃 발생 후 해당 패킷부터 다시 전송</p>
<h4 id="selective-repeat">Selective Repeat</h4>
<p>RDT 3.0의 구현 방식 중 하나 
sender는 N개의 데이터 패킷을 동시에 전송할 수 있음
<strong>손실되거나 오류가 발생한 패킷만 선택적으로 재전송함</strong></p>
<p><strong>특징</strong></p>
<ul>
<li>효율적: 손실된 패킷만 다시 전송하므로 네트워크 대역폭을 절약</li>
<li>복잡함: 각 패킷을 개별적으로 확인하고 저장해야 하므로 구현이 더 복잡</li>
</ul>
<p><strong>동작 방식</strong>
sender는 각 패킷에 대해 개별적으로 ACK를 확인함
손실된 패킷만 재전송하고, receiver는 버퍼를 사용하여 순서가 맞지 않는 패킷을 저장했다가 재조립</p>
<h2 id="tcp의-rdt">TCP의 RDT</h2>
<p>TCP는 RDT 3.0 기반의 신뢰성 있는 데이터 전송 프로토콜</p>
<ul>
<li><p><strong>오류 감지 및 복구</strong></p>
<ul>
<li>체크섬: 데이터가 손상되었는지 감지</li>
<li>ACK: receiver가 데이터를 정상적으로 받았음을 sender에게 알림
이때 시퀀스 넘버는 해당 넘버 이전 데이터까지 잘 받았으니 이 넘버부터 데이터를 보내라는 의미
예: ACK = N: N번 이전까지 잘 받았으니 N번부터 보내라</li>
<li>재전송: 오류가 발생한 패킷을 재전송</li>
</ul>
</li>
<li><p><strong>데이터 손실 및 재전송</strong></p>
<ul>
<li>타이머 기반 재전송
sender는 일정 시간 내에 ACCK를 받지 못하면 데이터가 손실된 것으로 간주하고 재전송</li>
<li>빠른 재전송 (Fast Retransmit)
3번 이상의 중복 ACK를 받으면, 타이머를 기다리지 않고 즉시 재전송</li>
</ul>
</li>
<li><p><strong>순서 보장</strong></p>
<ul>
<li>시퀀스 번호&quot; 패킷이 순서대로 도챡했는지 확인
(순서가 어긋난 패킷은 receiver 측 버퍼에 저장 후 올바른 순서대로 조립)</li>
</ul>
</li>
</ul>
<h2 id="tcp의-흐름제어">TCP의 흐름제어</h2>
<p><strong>흐름 제어(Flow Control)</strong>
송신자가 수신자의 데이터 처리 속도에 맞춰 데이터 전송 속도를 조절하는 메커니즘
수신자가 데이터를 너무 빠르게 받으면 버퍼가 가득 차고 데이터가 손실될 수 있기 때문에, TCP는 흐름 제어를 통해 데이터 전송을 조절</p>
<p><strong>방법</strong>
TCP는 윈도우 크기를 조절하여 흐름 제어를 수행
윈도우 크기는 수신자가 현재 받을 수 있는 데이터 크기를 의미하며, 송신자는 이 크기를 초과하지 않도록 데이터를 보냄</p>
<p><strong>Zero Window</strong> 문제
숫니자의 버퍼가 가득 차면 윈도우 크기를 0으로 설정하여 더 이상 데이터를 받을 수 없음을 송신자에게 알림
송신자는 새로운 윈도우 크기를 받을 때까지 데이터 전송을 멈춤
하지만, 만약 이 정보(윈도우 크기 0)가 네트워크에서 손실되면 송신자는 무한 대기에 빠질 수 있음
이를 방지하기 위해 TCP는 주기적으로 &quot;윈도우 크기 확인 패킷&quot;을 전송하여 수신자의 상태를 확인함</p>
<h2 id="tcp의-혼잡-제어">TCP의 혼잡 제어</h2>
<p><strong>혼잡 제어(Congestion Control)</strong>
네트워크의 트래픽 과부하를 방지하기 위해 송신자가 데이터 전송 속도를 조절하는 기법
한 사용자가 너무 많은 데이터를 보내면 다른 사용자에게 불이익이 발생할 수 있으므로 혼잡 제어를 통해 공정하게 네트워크를 사용할 수 있도록 함</p>
<h3 id="혼잡-제어-메커니즘">혼잡 제어 메커니즘</h3>
<ol>
<li><p>Slow Start
초기 패킷 전송량을 매우 작게 설정하고 2배씩 늘려가면서 네트워크 상태를 점검</p>
</li>
<li><p>Congestion Avoidance
패킷 전송량이 일정 threshold에 도달하면 증가 속도를 선형 증가로 변경</p>
</li>
<li><p>Fast Recovery 
패킷 손실이 발생했을 경우 대처</p>
<ul>
<li>동일 ACK를 3번 받은 경우(일부 패킷 손실)
네트워크 과부하 직전으로 판단 =&gt; Congestion Avoidance부터 재시작
threshold는 현재 전송량의 절반 값으로 설정</li>
<li>타이머에 의한 타임 아웃
네트워크 과부하 상황으로 판단 =&gt; Slow Start부터 재시작
threshold는 현재 전송량의 절반 값으로 설정</li>
</ul>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[응용 계층]]></title>
            <link>https://velog.io/@siha_014/%EC%9D%91%EC%9A%A9-%EA%B3%84%EC%B8%B5</link>
            <guid>https://velog.io/@siha_014/%EC%9D%91%EC%9A%A9-%EA%B3%84%EC%B8%B5</guid>
            <pubDate>Wed, 14 Jan 2026 13:49:58 GMT</pubDate>
            <description><![CDATA[<h1 id="응용-계층">응용 계층</h1>
<p>애플리케이션이 동작하는 계층
역할: 클라이언트의 요청을 전달하기 위해 통신 대상(서버 등)이 이해할 수 있는 메시지(데이터)로 변환하고 전송 계층으로 전달
클라이언트 측 애플리케이션(웹 브라우저, 메일 프로그램 등)이 서버 측 애플리케이션(웹 서버 프로그램, 메일 서버 프로그램 등)과 통신하려면 응용 계층의 프로토콜을 사용해야 함</p>
<p><strong>대표적 프로토콜</strong></p>
<ul>
<li><p><strong>HTTP/HTTPS</strong>
웹 브라우징에서 웹 서버와 클라이언트 간 데이터를 주고받음
HTTPS는 보안을 위해 TLS를 추가함</p>
</li>
<li><p><strong>DNS</strong>
사용자가 입력한 도메인 이름(예: <a href="http://www.example.com)%EC%9D%84">www.example.com)을</a> 해당하는 IP주소로 변환
네트워크 통신의 시작점</p>
</li>
<li><p>FTP
파일 업로드 및 다운로드에 사용
예: 대용량 파일 전송, 서버 파일 관리</p>
</li>
<li><p>SMTP, POP3
이메일 시스템의 핵심 프로토콜
SMTP: 이메일 송신 / POP3: 이메일 수신</p>
</li>
</ul>
<h2 id="http-hypertext-transfer-protocol">HTTP (HyperText Transfer Protocol)</h2>
<p>클라이언트와 서버 간에 웹 페이지와 리소스(이미지, 동영상 등)를 주고받기 위한 프로토콜</p>
<p><img src="https://velog.velcdn.com/images/siha_014/post/4c33d8bb-7a47-4173-bae2-4b114864d5e8/image.png" alt=""></p>
<h3 id="주요-특징">주요 특징</h3>
<ul>
<li><strong>비연결 지향 (Stateless)</strong>
HTTP는 요청과 응답이 끝나면 연결을 끊음
매 요청마다 새로운 연결을 생성하므로, 상태를 유지하지 않음
상태를 위해 쿠키, 세션, 토큰 등을 활용</li>
<li><strong>텍스트 기반 프로토콜</strong>
사람이 읽을 수 있는 형식으로 요청과 응답을 처리
예: GET /index.html HTTP/1.1</li>
<li><strong>요청-응답 방식</strong>
클라이언트가 요청(Request)를 보내면, 서버가 응답(Respose)를 반환</li>
<li><strong>리소스 중심</strong>
HTTP는 웹 서버의 리소스(URL)에 접근하기 위한 프로토콜
클라이언트는 서버에 리소스를 요청하고, 서버는 해당 리소스를 반환</li>
</ul>
<h3 id="메시지-구조">메시지 구조</h3>
<h4 id="http-요청">HTTP 요청</h4>
<p>: 요청줄, 헤더, 본문으로 구성</p>
<ul>
<li>요청줄: 메서드, 요청 대상(URL), 프로토콜 버전</li>
<li>헤더: 오청 관련 부가 정보</li>
<li>본문: 전송할 실제 데이터 (POST, PUT 등에서 사용)</li>
</ul>
<h4 id="http-응답">HTTP 응답</h4>
<p>: 상태줄, 헤더, 본문으로 구성
상태줄: 프로토콜 버전, 상태 코드, 상태 메시지
본문: 서버가 클라이언트에 전달하는 실제 데이터</p>
<h3 id="http-메서드">HTTP 메서드</h3>
<ul>
<li>GET: 리소스 조회</li>
<li>POST: 리소스 생성</li>
<li>PUT: 전체 수정</li>
<li>PATCH: 일부 수정</li>
<li>DELETE: 리소스 삭제</li>
</ul>
<h3 id="http-상태-코드">HTTP 상태 코드</h3>
<p>서버가 요청을 처리한 결과를 숫자로 표현한 코드</p>
<ul>
<li>1xx: 정보 (요청 진행 중)</li>
<li>2xx: 성공 (200 OK, 201 Created 등)</li>
<li>3xx: 리다이렉션 (301, 302, 304 등)</li>
<li>4xx: 클라이언트 오류 (400 Bad Request, 404 Not Found 등)</li>
<li>5xx: 서버 오류 (500 Internal Server Error 등)</li>
</ul>
<h3 id="버전별-특징">버전별 특징</h3>
<ul>
<li>HTTP/1.0
요청마다 새로운 연결을 생성 -&gt; 비효율적</li>
<li>HTTP/1.1
Keep-Alive로 연결 재사용 가능
Host 헤더 필수</li>
<li>HTTP/2
멀티 플렉싱 지원 -&gt; 하나의 연결로 여러 요청 병렬 처리
텍스트 대신 이진 프레임 구조
헤더 압축으로 속도 개선</li>
<li>HTTP/3
TCP대신 UDP 기반 QUIC 프로토콜 사용
연결 지연 감소, 전송 속도 향상</li>
</ul>
<h2 id="dns-domain-name-system">DNS (Domain Name System)</h2>
<p>도메인 이름을 IP주소로 변환하는 시스템
사용자가 기억하기 쉬운 도메인 이름(<a href="http://www.example.com)%EC%9D%84">www.example.com)을</a> 입력하면 이를 네트워크가 이해할 수 있는 IP 주소로 변환하여 통신이 가능하도록 함</p>
<h3 id="동작-과정">동작 과정</h3>
<ol>
<li>사용자가 도메인 이름 입력</li>
<li>로컬 DNS 캐시 확인
사용자의 컴퓨터가 최근 방문한 도메인의 IPP 주소를 캐시에 저장했는지 확인</li>
<li>DNS 재귀 서버 (Resolver)
캐시에 없는 경우, 인터넷 서비스 제공자 (ISP)의 DNS 서버로 요청이 전달됨
DNS 계층적 조회
요청순서: 루트 네임서버 &gt; TLD 네임서버 &gt; 권한 네임서버
<img src="https://velog.velcdn.com/images/siha_014/post/553a9622-01a0-4ae6-a0be-af426f7a5e23/image.png" alt=""></li>
<li>IP 주소 반환
사용자의 컴퓨터로 IP 주소가 전달되고, 해당 주소로 연결</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[네트워크 기초]]></title>
            <link>https://velog.io/@siha_014/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EA%B8%B0%EC%B4%88</link>
            <guid>https://velog.io/@siha_014/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EA%B8%B0%EC%B4%88</guid>
            <pubDate>Mon, 12 Jan 2026 10:57:21 GMT</pubDate>
            <description><![CDATA[<h1 id="네트워크">네트워크</h1>
<p>여러 장치(컴퓨터, 스마트폰 등)를 서로 연결해 데이터를 주고 받을 수 있게 만든 통신 구조
이를 이용해 이메일, 웹 브라우징, 파일 공유, 메신저 등 다양한 서비스를 이용할 수 있음</p>
<p><strong>호스트</strong>: 네트워크에서 데이터를 주고받는 객체
<strong>클라이언트</strong>:  서비스를 요청하는 장치
<strong>서버</strong>: 서비스를 제공하는 장치
<strong>라우터</strong>: 네트워크 간 데이터를 전달하는 장치 (최적의 경로를 선택하여 전송)
<strong>스위치</strong>: 같은 네트워크 내에서 데이터를 전달하는 장치
<strong>액세스 포인트(AP)</strong>: 무선 네트워크를 제공하는 장치
케이블 및 무선 매체
<strong>유선</strong>: 이더넷 케이블(LAN선)과 같은 물리적 연결
<strong>무선</strong>: Wi-Fi, 블루투스 등
<strong>IP 주소</strong>: 네트워크 상의 장치 위치를 나타내는 고유한 주소
<strong>MAC 주소</strong>: 네트워크 장치에 부여된 고유한 물리적 주소 (장치 식별에 사용)
<strong>방화벽</strong>: 네트워크 보안을 위한 장치 (외부 공격으로부터 보호)</p>
<h2 id="네트워크-종류---lan과-wan">네트워크 종류 - LAN과 WAN</h2>
<h3 id="lan-local-area-network">LAN (Local Area Network)</h3>
<p>제한된 지역 내(가정, 사무실, 학교 등)에서 사용하는 네트워크
가까운 거리에서 여러 장치(컴퓨터, 프린터, 스마트폰 등)를 연결하여 데이터를 주고받는 데 사용됨</p>
<ul>
<li>고속 데이터 전송: 짧은 거리에서 높은 속도로 통신 가능</li>
<li>자체 관리: 사용자가 네트워크를 설정하고 관리</li>
<li>저비용: 구축 및 유지 비용이 낮음</li>
</ul>
<p>사용 예: 회사 내 사내망, 가정에서 공유기로 연결된 네트워크</p>
<h3 id="wan-wide-area-network">WAN (Wide Area Network)</h3>
<p>넓은 범위를 연결하는 네트워크, 국가나 대륙을 넘는 광범위한 네트워크를 의미
여러 LAN을 연결해 하나의 큰 네트워크를 형성</p>
<ul>
<li>넓은 범위: 도시, 국가, 대륙 간 네트워크 연결</li>
<li>느린 속도: 거리와 데이터 양에 따라 LAN보다 속도가 느릴 수 있음</li>
<li>복잡한 관리: 인터넷 서비스 제공자(ISP)가 네트워크를 관리</li>
<li>고비용: 데이터 전송 장비와 유지 비용이 높음</li>
</ul>
<p>사용 예: 인터넷, 기업 간 데이터 센터 연결</p>
<h1 id="데이터-전송-방식">데이터 전송 방식</h1>
<h2 id="회선-교환-방식-circuit-switching">회선 교환 방식 (Circuit Switching)</h2>
<p>데이터를 전송하기 전에 송신자와 수신자 간에 고정된 통신 경로(회선)를 설정하고, 해당 경로를 통해 데이터를 전송하는 방식</p>
<p><strong>특징</strong></p>
<ul>
<li>독점 자원 사용: 데이터 전송 동안 다른 사용자가 해당 경로를 사용할 수 없음</li>
<li>연속적 데이터 흐름: 데이터가 순서대로 전달됨</li>
</ul>
<p><strong>장점</strong></p>
<ul>
<li>안정적이고 신뢰성이 높음(데이터 손실이 거의 없음)</li>
</ul>
<p><strong>단점</strong></p>
<ul>
<li>네트워크 자원 낭비 (사용자가 데이터를 전송하지 않아도 경로가 점유됨)</li>
<li>다수의 사용자에게 비효율적</li>
</ul>
<h2 id="패킷-교환-방식-packet-switching">패킷 교환 방식 (Packet Switching)</h2>
<p>데이터를 작은 단위인 패킷으로 나누어 전송하며, 각 패킷이 독립적으로 네트워크를 통해 목적지로 전달되는 방식</p>
<p><strong>특징</strong></p>
<ul>
<li>데이터 분할: 데이터를 작은 패킷으로 쪼갬</li>
<li>경로 동적 선택: 각 패킷이 네트워크 상태에 따라 서로 다른 경로로 전송 가능</li>
<li>효율성: 네트워크 자원을 필요할 때만 사용, 자원을 공유함</li>
<li>재조립 필요: 목적지에서 패킷을 모아 원래 데이터로 복원</li>
</ul>
<p><strong>장점</strong></p>
<ul>
<li>네트워크 자원을 효율적으로 사용 (자원이 여유로울 때 전송 가능)</li>
<li>여러 사용자가 동시에 네트워크를 이용 가능</li>
<li>데이터 전송 경로를 유연하게 선택</li>
</ul>
<p><strong>단점</strong></p>
<ul>
<li>패킷 손실이나 순서 꼬임이 발생할 수 있음</li>
<li>데이터 재조립 시간 소요</li>
</ul>
<p>사용 예: 인터넷(TCP/IP 기반의 통신), 이메일, 웹 브라우징 등</p>
<h3 id="패킷-교환-방식에서-발생하는-딜레이">패킷 교환 방식에서 발생하는 딜레이</h3>
<p><img src="https://velog.velcdn.com/images/siha_014/post/46503990-5782-45dd-9fa8-d80d1b527de2/image.png" alt=""></p>
<h4 id="처리-딜레이-processing-delay">처리 딜레이 (Processing Delay)</h4>
<p>정의: 네트워크 장치(라우터, 스위치 등)가 패킷을 처리하는 데 걸리는 시간
패킷의 헤더를 분석하고 목적지를 결정, 오류 검사 및 데이터 확인
특징: 일반적으로 매우 짧음</p>
<h4 id="큐잉-딜레이-queuing-delay">큐잉 딜레이 (Queuing Delay)</h4>
<p>정의: 패킷이 전송되기 전에 라우터나 스위치의 큐에서 대기하는 시간
네트워크 장치로 들어오는 패킷이 많아 대기열이 길어질 때 발생
트래픽이 많아지면 딜레이가 길어질 수 있음</p>
<h4 id="전송-딜레이transmission-delay">전송 딜레이(Transmission Delay)</h4>
<p>정의: 패킷의 데이터를 네트워크 링크로 내보내는 데 걸리는 시간
패킷 크기와 링크의 대역폭(전송 속도)에 따라 결정
특징: 링크 속도가 낮거나 패킷 크기가 크면 딜레이가 증가</p>
<h4 id="전파-딜레이propagation-delay">전파 딜레이(Propagation Delay)</h4>
<p>정의: 패킷이 네트워크 링크를 통해 실제로 전달되는 시간
링크의 물리적 길이(거리)에 따라 결정되고 대역폭과는 무관
특징: 거리가 멀수록 딜레이가 증가 (속도는 일정)</p>
<h1 id="프로토콜">프로토콜</h1>
<p>네트워크에서 데이터를 주고받는 규칙과 약속
서로 다른 장치가 효율적이고 정확하게 통신할 수 있도록 정해진 방식</p>
<p><strong>역할</strong></p>
<ul>
<li>데이터 구조 정의<ul>
<li>데이터를 어떤 형식으로 보낼지 정함 (예: 패킷의 구성 요소)</li>
<li>헤더, 본문, 오류 검사 데이터 등 포함</li>
</ul>
</li>
<li>전송 규칙 정의<ul>
<li>데이터가 어떻게 전송되고 확인될지를 정함</li>
<li>연결 설정, 데이터 흐름 관리, 오류 처리 등</li>
</ul>
</li>
<li>호환성 보장<ul>
<li>서로 다른 운영체제나 장치가 동일한 프로토콜을 사용할 수 있도록 보장</li>
</ul>
</li>
</ul>
<h1 id="osi-7계층과-tcpip-4계층">OSI 7계층과 TCP/IP 4계층</h1>
<p><img src="https://velog.velcdn.com/images/siha_014/post/bba8a2d6-f05b-45c0-8475-c1b0efbb85c9/image.png" alt=""></p>
<ul>
<li>데이터 전송 방식
데이터 송수신은 택배 시스템과 비슷하다.
데이터는 출발지(클라이언트)에서 목적지(서버)로 이동하는데, 
이때, 데이터를 포장하고, 경로를 설정하며, 배달하는 단계(계층)을 거쳐 이동한다.</li>
</ul>
<ul>
<li>프로토콜은 각 단계에서 사용하는 도구로써, HTTP, TCP, IP 같은 프로토콜이 단계별로 데이터를 처리한다.</li>
</ul>
<ul>
<li><p>OSI 7계층과 TCP/IP 4계층은 이런 과정을 체계적으로 설명함</p>
<ul>
<li>데이터를 송수신할때 어떤 순서로 통과하는지 보여줌</li>
<li>&quot;데이터는 응용 계층에서 생성되어 전송 계층, 인터넷 계층을 거쳐 실제 네트워크로 나간다&quot;</li>
</ul>
</li>
<li><p>OSI 7계층과 TCP/IP 4계층의 차이점</p>
<ul>
<li>OSI 7계층: 이론적이고 교육적인 참조 모델</li>
<li>TCP/IP 4계층: 인터넷 통신의 실용적인 구현 모델</li>
</ul>
</li>
</ul>
<h2 id="osi-7계층">OSI 7계층</h2>
<p><img src="https://velog.velcdn.com/images/siha_014/post/7469c62c-aef5-42a6-8c44-b74bf33c3f78/image.png" alt=""></p>
<h3 id="응용-계층">응용 계층</h3>
<p>사용자와 가장 가까운 계층, 네트워크 서비스를 직접 제공함
예: 웹 브라우징(HTTP), 파일 전송(FTP), 이메일(SMTP)</p>
<h3 id="표현-계층">표현 계층</h3>
<p>데이터의 형식을 변환하여 송신자와 수신자가 동일한 데이터 형식을 사용할 수 있게 함
데이터 암호화(SSL/TLS), 압축, 인코딩/디코딩 수행</p>
<h3 id="세션-계층">세션 계층</h3>
<p>두 장치 간 세션(연결)을 설정, 유지, 종료
데이터 동기화와 체크포인트 제공</p>
<h3 id="전송-계층">전송 계층</h3>
<p>데이터가 신뢰성 있게 전달되도록 보장
TCP: 연결 지향, 데이터 신뢰성 보장
UDP: 비연결 지향, 빠른 데이터 전송</p>
<h3 id="네트워크-계층">네트워크 계층</h3>
<p>데이터를 목적지까지 전달하기 위한 경로 설정과 패킷 전달 관리
IP 주소를 기반으로 통신 수행</p>
<h3 id="데이터-링크-계층">데이터 링크 계층</h3>
<p>물리 계층에서 데이터를 프레임으로 변환하고, 네트워크 계층으로 전달
오류 검출, 수정 및 흐름 제어 수행
MAC 주소를 사용하여 네트워크 내 장치 식별</p>
<h3 id="물리-계층">물리 계층</h3>
<p>데이터를 실제 신호(전기, 광, 무선)로 변환하여 전송
케이블, 커넥터, 주파수 등 물리적 매체와 관련됨</p>
<blockquote>
<p><strong>헤더</strong>: 데이터를 보낼 때 추가해야하는 정보
<strong>캡슐화</strong>: 데이터를 상위 계층에서 하위 계층으로 내려보내며 각 계층에서 헤더를 추가함, 최종적으로 물리 계층에서 신호로 변환되어 전송
<strong>역캡슐화</strong>: 데이터를 수신 측에서 하위 계층에서 상위 계층으로 올리면서 각 계층의 헤더를 제거</p>
</blockquote>
<p><strong>OSI 모델의 핵심 원리</strong></p>
<ul>
<li>계층별 독립성: 각 계층은 독립적으로 동작하며, 다른 계층과는 인터페이스만 정의됨</li>
<li>상호 운용성: 서로 다른 시스템과 장치 간 통신이 가능하도록 표준화된 구조 제공</li>
<li>문제 분리: 특정 계층에서 발생한 문제를 쉽게 식별하고 해결 가능</li>
</ul>
<h2 id="tcpip-4계층">TCP/IP 4계층</h2>
<p><img src="https://velog.velcdn.com/images/siha_014/post/6a0a1681-492d-4350-91b2-cd6a668d8d78/image.png" alt=""></p>
<h3 id="응용-계층-1">응용 계층</h3>
<p>사용자가 네트워크와 직접 상호작용하는 계층
데이터를 요청하거나 응답하는 역할 수행
OSI 모델의 응용 계층, 표현 계층, 세션 계층 기능을 통합
예시 프로토콜: HTTP, FTP, SMTP, DNS(도메인 이름을 IP 주소로 변환)</p>
<h3 id="전송-계층-1">전송 계층</h3>
<p>데이터 전송의 신뢰성과 속도 관리
송신 측과 수신 측 간에 데이터의 흐름 제어 및 오류 검출 수행
OSI 모델의 전송 계층에 해당
예시 프로토콜: TCP, UDP</p>
<h3 id="인터넷-계층">인터넷 계층</h3>
<p>데이터를 목적지까지 전달하기 위한 경로 설정과 패킷 전송 관리
OSI 모델의 네트워크 계층에 해당
예시 프로토콜: IP, ARP</p>
<h3 id="네트워크-인터페이스-계층">네트워크 인터페이스 계층</h3>
<p>데이터를 물리적 신호로 변환하고 전송
OSI 모델의 데이터 링크 계층, 물리 계층에 해당
예시 기술: 유선 네트워크(Ethernet), 무선 네트워크(Wi-Fi)</p>
<h2 id="캡슐화와-역캡슐화">캡슐화와 역캡슐화</h2>
<p><img src="https://velog.velcdn.com/images/siha_014/post/18554b76-9645-462e-a8c7-e6dcd43e866c/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[파일 시스템 & 디스크 관리]]></title>
            <link>https://velog.io/@siha_014/%ED%8C%8C%EC%9D%BC-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EB%94%94%EC%8A%A4%ED%81%AC-%EA%B4%80%EB%A6%AC</link>
            <guid>https://velog.io/@siha_014/%ED%8C%8C%EC%9D%BC-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EB%94%94%EC%8A%A4%ED%81%AC-%EA%B4%80%EB%A6%AC</guid>
            <pubDate>Sat, 10 Jan 2026 08:41:54 GMT</pubDate>
            <description><![CDATA[<h1 id="파일-시스템-file-system">파일 시스템 (File System)</h1>
<p>저장 장치에서 데이터를 관리하고 조직화하는 체계로, 운영체제가 파일을 생성, 읽기, 쓰기, 삭제 등의 작업을 할 수 있도록 지원
데이터가 파일 단위로 저장되고, 디렉토리를 통해 계층적으로 관리됨</p>
<h4 id="파일">파일</h4>
<p>데이터의 집합, 저장 장치에 이름과 속성을 가지고 저장되는 단위
사용자는 파일을 통해 데이터를 저장하거나 읽을 수 있음
구성요소: 파일 이름, 데이터, 속성</p>
<h4 id="파일-속성">파일 속성</h4>
<p>파일에 대한 메타데이터, 파일의 상태와 동작 방식을 정의하는 정보
파일 이름, 크기, 유형, 위치, 권한, 생성/수정 날짜, 소유자 등의 속성을 지님</p>
<h4 id="디렉토리">디렉토리</h4>
<p>파일 및 디렉토리를 포함하는 구조, 파일을 체계적으로 관리하기 위한 공간
파일 그룹화로 파일을 논리적으로 정리하거나, 계층적 구조를 제공하고, 파일을 탐색할 수 있다.</p>
<h4 id="파티션">파티션</h4>
<p>저장 장치를 논리적으로 나눈 영역으로, 각 파티션에 파일 시스템을 생성하여 데이터를 저장
하나의 디스크를 여러 파티션으로 나눌 수 있음
운영체제, 사용자 데이터, 백업 등을 분리하여 관리할 수 있다.</p>
<h2 id="파일-보호">파일 보호</h2>
<p>파일에 저장된 데이터를 무단 접근, 수정, 삭제로부터 보호하기 위한 메커니즘
파일의 경우 여러 사용자가 사용할 수 있기 때문에 각 파일에 대해 누구에게 어떤 권한을 허용할 것인가가 필요</p>
<h3 id="필요성">필요성</h3>
<ul>
<li>데이터 무결성 유지
파일이 구너한 없는 사용자에 의해 수정/삭제되는 것을 방지</li>
<li>정보 기밀성 보장
중요한 정보가 권한이 없는 사람에게 노출되지 않도록 보호</li>
</ul>
<h3 id="파일-권한-설정-방법">파일 권한 설정 방법</h3>
<h4 id="access-control-matrix">Access Control Matrix</h4>
<p>사용자와 파일 간의 권한(Permissions)를 정의한 행렬 구조
사용자 및 파일 수가 많아질수록 행렬 크기가 커져 관리가 어려워짐</p>
<h4 id="grouping">Grouping</h4>
<p>사용자를 소유자(Owner), 그룹(Group), 기타(Other)로 구분
각 파일에서 세 그룹에 대한 권한을 설정 (그룹마다 3비트로 총 9비트로 표현)</p>
<blockquote>
<p>r: 읽기, w: 쓰기, x: 실행</p>
</blockquote>
<ul>
<li>Owner: rwx (읽기, 쓰기, 실행 가능)</li>
<li>Group: rw- (읽기, 쓰기 가능)</li>
<li>Other: r-x (읽기, 실행 가능)</li>
</ul>
<h4 id="password">Password</h4>
<p>파일마다 비밀번호를 설정하고, 비밀번호를 아는 사람만 접근 가능
파일이 많아지면 비밀번호 관리가 힘들다는 단점이 있음</p>
<h2 id="파일-할당-방식">파일 할당 방식</h2>
<h3 id="연속-할당-continuous-allocation">연속 할당 (Continuous Allocation)</h3>
<p>파일의 데이터를 연속된 블록에 저장
시작 블록 번호와 파일 크기를 기반으로 데이터의 저장 위치를 관리한다.
초기 파일 시스템에서 사용되었음</p>
<p><strong>장점</strong></p>
<ul>
<li>빠른 접근 속도: 연속된 블록에 저장되어 랜덤 및 순차 접근 모두 빠름</li>
<li>구현 간단: 시작 위치와 크기만 알면 접근 가능</li>
</ul>
<p><strong>단점</strong></p>
<ul>
<li>외부 단편화: 파일 삭제 후 남는 공간이 사용되지 못하는 문제</li>
<li>파일 크기 확장 어려움: 기존 연속 블록 뒤에 여유 공간이 없으면 확장 불가</li>
</ul>
<h3 id="연결-할당-linked-allocation">연결 할당 (Linked Allocation)</h3>
<p>파일의 데이터를 임의의 빈 블록에 저장하고, 각 블록이 다음 블록을 가리키는 포인터를 포함. 포인터를 통해 파일의 데이터를 연결</p>
<p><strong>장점</strong></p>
<ul>
<li>외부 단편화 문제 해결: 연속된ㄴ 공간이 필요하지 않음</li>
<li>파일 크기 확장 요이: 새로운 블록을 추가로 연결하면 됨</li>
</ul>
<p><strong>단점</strong></p>
<ul>
<li>랜덤 접근 속도 느림: 파일의 특정 위치에 접근하려면 처음 블록부터 포인터를 따라가야 함</li>
<li>포인터 오버헤드: 각 블록에 포인터 공간이 추가로 필요</li>
</ul>
<h3 id="색인-할당-indexed-allocation">색인 할당 (Indexed Allocation)</h3>
<p>파일의 모든 블록 번호를 색인 블록(Index Block)에 저장
색인 블록은 파일 데이터를 가리키는 블록 번호의 리스트를 포함
Unix/Linux에서 쓰이는 방식</p>
<p><strong>장점</strong></p>
<ul>
<li>랜덤 접근 속도 빠름: 색인 블록에서 블록 번호를 바로 확인 가능</li>
<li>외부 단편화 문제 없음: 데이터는 임의의 빈 블록에 저장</li>
</ul>
<p><strong>단점</strong></p>
<ul>
<li>색인 블록 오버헤드: 색인 블록을 위한 추가 저장 공간 필요</li>
<li>큰 파일의 색인 제한: 색인 블록 크기가 고정적이면 큰 파일 관리가 어려움</li>
</ul>
<hr>
<h1 id="디스크-관리">디스크 관리</h1>
<h2 id="하드-디스크의-데이터-구성-요소">하드 디스크의 데이터 구성 요소</h2>
<h3 id="데이터-구조">데이터 구조</h3>
<p><img src="https://velog.velcdn.com/images/siha_014/post/9f78b393-98cb-48d5-bbaa-e8b737575d50/image.png" alt=""></p>
<ul>
<li>트랙(Track): 플래터 표면에 도넛 모양으로 나뉜 데이터 저장 구역</li>
<li>섹터(Sector): 트랙을 더 작은 데이터 블록으로 나눈 구역 -&gt; 데이터는 섹터 단위로 읽거나 써짐</li>
<li>실린더(Cylinder): 층층이 쌓인 플래터에서 걸쳐 동일한 트랙 번호를 연결한 3차원적인 구조</li>
</ul>
<h3 id="데이터-접근-시간--탐색-시간--회전-지연-시간--데이터-전송-시간">데이터 접근 시간 = 탐색 시간 + 회전 지연 시간 + 데이터 전송 시간</h3>
<ul>
<li><strong>탐색 시간</strong>: 디스크 암을 움직여 특정 트랙으로 헤더를 이동시키는 시간</li>
<li><strong>회전 지연 시간</strong>: 디스크가 회전하여 헤더가 읽기/쓰기 대상이 되는 섹터 위에 위치하게 되는 시간</li>
<li><strong>데이터 전송 시간</strong>: 읽기/쓰기 헤드가 데이터를 실제로 읽거나 쓰는데 걸리는 시간</li>
</ul>
<h4 id="데이터-접근-시간-최적화">데이터 접근 시간 최적화</h4>
<ul>
<li>상대적으로 가장 길로 통제 가능한 탐색 시간을 줄이는 것이 핵심 (암의 움직임 최소화)</li>
<li>가능한 동일한 실린더에 데이터를 적재하여 탐색 시간을 줄임</li>
<li>연속적으로 데이터에 접근할 때 적절한 스케줄링 기법을 사용하여 탐색 시간을 최소화</li>
</ul>
<h2 id="디스크-스케줄링-기법">디스크 스케줄링 기법</h2>
<p>탐색 시간을 최소화하는 것이 핵심
I/O 요청을 처리하기 위해 어떤 순서로 트랙에 접근할지를 결정하는 알고리즘</p>
<h3 id="fcfs-first-come-first-serve">FCFS (First Come First Serve)</h3>
<p>요청 순서대로 처리
장점: 구현이 간단
단점: 헤드 이동 거리가 길어질 수 있음 -&gt; 비효율적</p>
<h3 id="sstf-shortest-seek-time-first">SSTF (Shortest Seek Time First)</h3>
<p>현재 헤드 위치에서 가장 가까운 요청을 먼저 처리
장점: 헤드 이동 최소화
단점: 기아(Starvation) 가능성 (가까운 요청만 처리, 먼 요청은 무한 대기)</p>
<h3 id="scan-elevator-algorithm">SCAN (Elevator Algorithm)</h3>
<p>디스크 헤드가 한쪽 끝으로 이동하며 요청을 처리한 뒤, 방향을 바꿔 반대쪽 끝으로 이동하며 처리
장점: 헤드 이동이 균형 있게 이루어짐
단점: 끝에 있는 요청은 대기 시간이 길어질 수 있음</p>
<h3 id="c-scan-circular-scan">C-SCAN (Circular SCAN)</h3>
<p>SCAN과 비슷하지만, 한쪽 끝에 도달하면 반대쪽 끝으로 바로 이동해 처리
장점: 요청 대기 시간이 균등해짐
단점: 끝으로 이동하는 동안 낭비가 발생</p>
<h3 id="look">LOOK</h3>
<p>SCAN과 유사하지만, 끝까지 가지 않고 요청이 있는 범위까지만 이동
장점: 불필요한 헤드 이동 감소
단점: 요청 분포에 따라 성능 차이</p>
<h3 id="c-look">C-LOOK</h3>
<p>C-SCAN과 유사하지만, 요청이 있는 범위까지만 이동한 뒤 반대쪽 끝으로 이동
장점: 헤드 이동이 최소화되고, 대기 시간 균등</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[메모리 관리 & 가상 메모리]]></title>
            <link>https://velog.io/@siha_014/%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B4%80%EB%A6%AC-%EA%B0%80%EC%83%81-%EB%A9%94%EB%AA%A8%EB%A6%AC</link>
            <guid>https://velog.io/@siha_014/%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B4%80%EB%A6%AC-%EA%B0%80%EC%83%81-%EB%A9%94%EB%AA%A8%EB%A6%AC</guid>
            <pubDate>Fri, 09 Jan 2026 13:52:22 GMT</pubDate>
            <description><![CDATA[<h2 id="ram과-주소-공간">RAM과 주소 공간</h2>
<p><strong>RAM (Random Access Memory)</strong>
주기억 장치의 주요 구성 요소로, 실행 중인 프로그램과 데이터를 저장해 CPU가 빠르게 접근 가능
데이터를 읽고 쓰는 속도가 빠른 휘발성 메모리
임의 접근 가능 = 어느 위치든 동일한 시간에 접근 가능
RAM 용량이 클수록 한 번에 많은 프로그램을 RAM에 적재하여 동시에 실행할 수 있음</p>
<p>주소 공간
: 메모리의 각 위치를 식별하는 고유한 주소의 범위
메모리의 주소 강간은 물리 주소 또는 논리 주소로 표현됨</p>
<p><strong>물리 주소 (Physical Address)</strong>: 실제 메모리의 물리적인 위치
<strong>논리 주소 (Logical Address)</strong>: CPU에서 참조하는 가상의 주소
CPU와 운영체제는 논리 주소를 물리 주소로 변환하여 데이터에 접근</p>
<p>프로그램이 다른 물리 주소를 갖게 되더라도 CPU에서는 항상 동일한 논리 주소를 참조할 수 있고, 
프로그램에 독립적인 논리 주소 공간을 할당하여 다른 프로그램의 메모리의 침범을 방지할 수 있어 논리 주소가 필요하다.</p>
<p><img src="https://velog.velcdn.com/images/siha_014/post/25f8dc51-5889-493a-a869-2aa261398b0b/image.png" alt=""></p>
<h2 id="mmu의-주소-변환">MMU의 주소 변환</h2>
<h3 id="mmu-memmory-management-unit">MMU (Memmory Management Unit)</h3>
<p>CPU가 메모리에 접근하는 것을 관리하는 하드웨어 부품
2개의 레지스터 정보를 사용하여 메모리 접근을 관리함</p>
<ul>
<li>베이스 레지스터 (Base register): 현재 프로그램의 메모리 시작 위치를 저장</li>
<li>한계 레지스터 (Limit register): 현재 프로그램의 메모리 크기(범위)를 저장</li>
</ul>
<h4 id="주소-변환">주소 변환</h4>
<p>물리 주소 = 베이스 레지스터 + 논리 주소</p>
<h4 id="접근-검증">접근 검증</h4>
<p>변환된 물리 주소가 베이스 레지스터, 베이스 레지스터 + 한계 레지스터 범위 내에 있는지 확인
만약 범위를 벗어나면 침범 탐지(트랩 또는 예외 발생) 후 접근을 차단</p>
<h2 id="프로그램-실행-시-메모리-최적화-기법">프로그램 실행 시 메모리 최적화 기법</h2>
<h3 id="동적-로딩-dynamic-loading">동적 로딩 (Dynamic Loading)</h3>
<p>전체 프로그램을 한꺼번에 메모리에 적재하지 않고, 프로그램 실행 중 필요한 모듈/데이터를 실시간으로 메모리에 적재하는 기술
개발자가 직접 제어하며, 명시적인 코드 작성이 필요하다.</p>
<h4 id="장점">장점</h4>
<ul>
<li>메모리 사용 감소</li>
<li>시작 속도 향상 (초기 로딩 시간 단축)</li>
<li>유연한 업데이트 가능<h4 id="단점">단점</h4>
</li>
<li>로드/언로드 시점 관리 필요(메모리 누수 위험)</li>
<li>코드 복잡도 증가</li>
</ul>
<h3 id="동적-연결-dynamic-linking">동적 연결 (Dynamic Linking)</h3>
<p>프로그램 실행 시 외부 라이브러리(함수, 코드)를 연결하는 기술
OS가 자동 관리하며, 실행 파일 크기를 줄임
정적 연결(Static Linking)에서는 컴파일 시 라이브러리를 프로그램에 포함하지만, 동적 연결은 실행 중 필요한 시점에 라이브러리를 로드하여 연결한다.</p>
<h4 id="장점-1">장점</h4>
<ul>
<li>실행 파일 크기 축소</li>
<li>라이브러리 업데이트 시 재 컴파일 불필요</li>
<li>여러 프로그램이 동일한 라이브러리를 공유하여 메모리 사용 효율 증가</li>
</ul>
<h1 id="메모리-할당-방식">메모리 할당 방식</h1>
<p>메모리의 낮은 주소 영역 -&gt; 운영체제 커널이 상주
메모리의 높은 주소 영역 -&gt; 사용자 프로그램이 올라가게 됨
사용자 프로그램을 메모리에 할당하는 방식이 나뉨</p>
<h2 id="연속-할당">연속 할당</h2>
<p>프로세스가 사용하는 메모리 공간이 연속된 블록으로 할당되는 방식
하나의 프로세스는 메모리의 연속된 범위를 독점적으로 사용CPU가 메모리에 접근하는 것을 관리하는 하드웨어 부품</p>
<blockquote>
<p>내부 조각: 프로그램 적재 뒤 남게 되는 메모리 공간
외부 조각: 해당 분할이 비어있는데도 프로그램을 적재하지 못해 낭비되논 메모리 공간</p>
</blockquote>
<h3 id="독점-분할-방식">독점 분할 방식</h3>
<p>메모리를 고정된 크기로 나눠 할당 (각각의 크기는 상이할 수 있음)
내부 조각, 외부 조각 문제가 발생할 수 있음</p>
<h3 id="가변-분할-방식">가변 분할 방식</h3>
<p>프로그램에 따라 분할의 크기, 개수가 동적으로 변하는 방식
내부 조각이 발생하지 않지만 외부 조각 문제가 발생할 수 있음</p>
<h3 id="프로그램을-적재할-메모리-공간을-선택하는-알고리즘">프로그램을 적재할 메모리 공간을 선택하는 알고리즘</h3>
<ul>
<li>first-fit: 가장 처음으로 발견된 가용 공간에 메모리를 할당</li>
<li>best-fit: 프로세스가 들어갈 수 있는 빈 공간 중, 크기가 가장 작은 공간에 메모리 할당</li>
</ul>
<h2 id="불연속-할당">불연속 할당</h2>
<h3 id="페이징-기법">페이징 기법</h3>
<p>프로세스를 고정된 크기의 <strong>페이지</strong>로 나누어 메모리에 할당하는 방식
프로세스를 연속된 공간에 배치하지 않고, 물리 메모리의 빈 공간을 효율적으로 활용</p>
<blockquote>
<p><strong>페이지</strong>: 프로세스의 논리 주소 공간을 일정 크기로 나눈 단위
<strong>프레임</strong>: 물리 메모리를 일정 크기(페이지와 동일한 크기)로 나눈 단위
<strong>페이지 테이블</strong>: 프로세스의 페이지와 물리 메모리 프레임 간의 매핑 정보를 저장하고, 각 페이지가 어느 프레임에저장되었는지 정보를 제공한다.</p>
</blockquote>
<h4 id="페이지-테이블의-주소-변환-기법">페이지 테이블의 주소 변환 기법</h4>
<ul>
<li>CPU가 사용하는 논리적 주소를 페이지 번호와 페이지 오프셋으로 나누어 주소 변환에 사용</li>
<li>페이지 번호를 페이지 테이블 접근의 인덱스로 사용
  -&gt; 해당 페이지의 물리적 메모리상의 기준 주소를 얻음</li>
<li>페이지 오프셋은 하나의 페이지 내에서의 변위를 의미</li>
<li>기준 주소값에 변위를 더함으로써 요청된 논리적 주소에 대응하는 물리적 주소를 얻음</li>
</ul>
<h4 id="페이지-테이블의-구현">페이지 테이블의 구현</h4>
<p>페이지 테이블은 물리적 메모리에 위치
운영체제는 2개의 레지스터를 사용하여 프로세스의 페이지 테이블에 접근</p>
<h4 id="2개의-레지스터">2개의 레지스터</h4>
<p>1) 페이지 테이블 베이스 레지스터 (PTBR)
기능: 현재 실행 중인 프로세스의 페이지 테이블 시작 주소를 저장
역할: CPU가 페이지 테이블에 접근할 수 있도록 기준 주소 제공</p>
<p>2) 페이지 테이블 길이 레지스터 (PTLR)
기능: 페이지 테이블의 엔트리 개수(크기)를 저장
역할: 주소 변환 시, 잘못된 페이지 번호에 접근하지 않도록 보호</p>
<h4 id="tlb-translation-look-aside-buffer">TLB (Translation Look-aside Buffer)</h4>
<p>: 페이지 테이블의 일부를 캐싱하는 고속 메모리
페이지 테이블 접근 시간을 줄이기 위해 자주 참조되는 페이지-프레임 매핑 정보를 저장</p>
<ul>
<li>TLB Hit
TLB에 페이지 번호와 매핑된 프레임 번호가 이미 저장되어 있으면 빠르게 변환
페이지 테이블 접근 없이 즉시 물리 주소로 변환 가능</li>
<li>TLB Miss
TLB에 해당 페이지 번호가 없을 경우, 페이지 테이블에 접근하여 변환
변환된 정보는 TLB에 저장하여 이후 접근을 가속화</li>
</ul>
<h3 id="세그멘테이션">세그멘테이션</h3>
<p>논리적으로 나뉜 세그먼트(Segment)로 분할하여 메모리에 할당하는 방식
페이징과 달리, 고정 크기가 아닌 가변 크기로 분할되며, 프로그램의 논리적 구조(코드, 데이터, 스택 등)를 반영하는 것이 특징</p>
<blockquote>
<p><strong>세그먼트</strong>: 프로그램의 논리적인 구성 요소
<strong>세그먼트 번호</strong>: 세그먼트를 식별하는 번호
<strong>오프셋</strong>: 해당 세그먼트 내의 위치</p>
</blockquote>
<h4 id="세그먼트-테이블의-구조">세그먼트 테이블의 구조</h4>
<ul>
<li>한계(limit): 세그먼트 크기</li>
<li>기준(base): 세그먼트 시작 주소</li>
</ul>
<h4 id="장점-2">장점</h4>
<ul>
<li>프로그램의 코드, 데이터, 스택 등을 논리적 단위로 관리</li>
<li>가변 크기 세그먼트를 사용하여 메모리 낭비를 줄임</li>
</ul>
<h4 id="단점-1">단점</h4>
<ul>
<li>외부 조각 발생 가능<ul>
<li>세그먼트 크기가 가변적이므로 메모리 공간에 빈 조각이 생길 수 있음</li>
</ul>
</li>
<li>주소 변환 오버헤드<ul>
<li>세그먼트 테이블을 조회하는데 추가 시간이 소요</li>
</ul>
</li>
</ul>
<hr>
<h1 id="가상-메모리-virtual-memory">가상 메모리 (Virtual Memory)</h1>
<p>물리적 메모리 (RAM)의 크기와 관계 없이, 실행 중인 프로세스가 사용할 수 있는 주소 공간을 확장하는 메모리 관리 기법
실제로 물리 메모리에 없는 부분은 디스크의 스왑 공간(Swap Space)을 활용하여 실행</p>
<h2 id="요구-페이징-demand-paging">요구 페이징 (Demand Paging)</h2>
<p>가상 메모리 기법 중 하나로, 프로세스 실행 시 필요한 페이지만 메모리에 로드하여 메모리 사용 효율을 높임</p>
<h3 id="작동-원리">작동 원리</h3>
<ul>
<li>프로세스가 실행되면, 페이지 테이블이 초기화됨</li>
<li>페이지 테이블에는 모든 페이지가 &quot;메모리에 없음&quot;으로 표시</li>
<li>CPU가 프로세스의 가상 주소에 접근했을 때, 해당 페이지가 메모리에 없으면 <strong>페이지 폴트(Page Fault)</strong> 발생<ul>
<li>페이지 폴트(Page Fault): 프로세스가 참조하는 페이지가 메모리에 없는 경우 발생</li>
</ul>
</li>
<li>운영체제는 디스크에서 해당 페이지를 메모리로 가져오고 페이지 테이블을 갱신</li>
<li>페이지가 메모리에 올라오면 정상적으로 실행</li>
</ul>
<h2 id="페이지-교체-알고리즘">페이지 교체 알고리즘</h2>
<p>가상 메모리 시스템에서 메모리가 부족할 때 어떤 페이지를 교체할지 결정하는 기법
페이지 폴트가 발생했을 때, 물리 메모리가 가득 차 있으면 새로운 페이지를 로드하기 위해 기존의 페이지를 내보내야 할 때 사용됨</p>
<h3 id="fifofirst-in-first-out">FIFO(First-In-First-Out)</h3>
<p>가장 먼저 메모리에 들어온 페이지를 가장 먼저 교체
운영체제가 페이지의 도착 순서를 관리하며, 큐(Queue)를 사용해 구현</p>
<h4 id="장점-3">장점</h4>
<ul>
<li>구현이 간단하고 직관적<h4 id="단점-2">단점</h4>
</li>
<li>페이지가 오래된 순서로 제거되므로, 실제로 자주 사용되는 페이지가 교체될 가능성 있음</li>
<li>벨라디의 역설 (Belady&#39;s Anomaly) 발생: 메모리 크기를 늘려도 페이지 폴트가 증가할 수 있음</li>
</ul>
<h3 id="opt-optimal-page-replacement">OPT (Optimal Page Replacement)</h3>
<p>가장 오랫동안 사용되지 않을 페이지를 교체
이론적으로 가장 효율적이지만, 미래의 참조를 예측해야 하므로 실제 시스템에서는 구현 불가능</p>
<h4 id="장점-4">장점</h4>
<ul>
<li>페이지 폴트를 최소화<h4 id="단점-3">단점</h4>
</li>
<li>미래 참조를 알 수 없기 때문에 실제 구현이 어려움</li>
</ul>
<h3 id="lru-least-recently-used">LRU (Least Recently Used)</h3>
<p>가장 오래 사용되지 않은 페이지를 교체
운영체제가 페이지의 참조 시간을 기록하여 가장 오래 사용하지 않은 페이지를 선택</p>
<h4 id="장점-5">장점</h4>
<ul>
<li>OPT 알고리즘에 근접한 성능</li>
<li>실제 구현 가능<h4 id="단점-4">단점</h4>
</li>
<li>참조 시간을 기록해야 하므로, 구현이 복잡하고 추가적인 오버헤드 발생</li>
<li>참조 횟수를 고려하지 않음</li>
</ul>
<h3 id="lfu-least-frequently-used">LFU (Least Frequently Used)</h3>
<p>참조 횟수가 가장 적은 페이지를 교체
각 페이지의 참조 횟수를 카운트하여 교체 기준으로 사용</p>
<h4 id="장점-6">장점</h4>
<ul>
<li>자주 사용되는 페이지를 우선적으로 유지<h4 id="단점-5">단점</h4>
</li>
<li>최근에 자주 사용된 페이지가 아닌 과거의 참조 횟수가 높은 페이지가 유지될 수 있음<h3 id="second-chance-clock-algorithm">Second-Chance (Clock Algorithm)</h3>
페이지에 참조 비트 (Reference Bit)를 사용하여 두 번의 기회를 부여
각 페이지가 메모리에 적재되거나 접근될 때 페이지의 참조 비트가 1로 설정됨
포인터가 페이지를 시계 방향으로 순회하며 참조 비트를 확인
참조 비트가 1이면 비트를 0으로 바꾸고 교체를 연기, 0이면 페이지를 교체
페이징 시스템에서 실제로 쓰이는 알고리즘<h4 id="장점-7">장점</h4>
</li>
<li>FIFO보다 페이지 폴트를 줄임</li>
<li>구현이 비교적 간단</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로세스 동기화와 데드락]]></title>
            <link>https://velog.io/@siha_014/%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EB%8F%99%EA%B8%B0%ED%99%94%EC%99%80-%EB%8D%B0%EB%93%9C%EB%9D%BD</link>
            <guid>https://velog.io/@siha_014/%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EB%8F%99%EA%B8%B0%ED%99%94%EC%99%80-%EB%8D%B0%EB%93%9C%EB%9D%BD</guid>
            <pubDate>Thu, 08 Jan 2026 12:52:43 GMT</pubDate>
            <description><![CDATA[<h1 id="프로세스-동기화와-데드락">프로세스 동기화와 데드락</h1>
<p>운영체제에서 <strong>프로세스 동기화(Process Synchronization)</strong>란  
여러 프로세스나 스레드가 <strong>공유 자원(shared resource)</strong>을 사용할 때<br>데이터의 <strong>일관성과 정확성</strong>을 보장하도록 실행 순서를 제어하는 것을 의미한다.</p>
<p>동기화가 제대로 이루어지지 않으면<br>Race Condition, Critical Section 문제, Deadlock과 같은 문제가 발생할 수 있다.</p>
<hr>
<h2 id="프로세스-동기화가-필요한-이유">프로세스 동기화가 필요한 이유</h2>
<p>여러 프로세스 또는 스레드가 동시에 실행되는 환경에서는<br>다음과 같은 상황이 발생할 수 있다.</p>
<ul>
<li>여러 실행 흐름이 하나의 공유 자원에 동시에 접근</li>
<li>실행 순서가 매번 달라질 수 있음</li>
<li>운영체제가 미세한 실행 타이밍까지 완벽하게 제어할 수 없음</li>
</ul>
<p>이로 인해 <strong>의도하지 않은 결과</strong>가 발생할 수 있으며,<br>이를 대표적으로 설명하는 개념이 <strong>Race Condition</strong>이다.</p>
<hr>
<h2 id="race-condition">Race Condition</h2>
<p>두 개의 연산이 동시에 공유 자원에 접근할 때,
결과가 일관성을 보장받지 못하는 상태</p>
<p>Race Condition은 다음 두 조건이 동시에 충족될 때 발생</p>
<ul>
<li>공유 자원 사용: 여러 스레드/프로세스가 동일한 자원을 동시에 사용함</li>
<li>동시성: 두 스레드/프로세스가 동시에 실행되며, 실행 순서를 운영체제가 제어하지 못함</li>
</ul>
<p>Race Condition은 단순한 버그가 아니라<br><strong>동기화가 없는 동시 실행 환경</strong>에서 필연적으로 발생할 수 있는 문제이다.</p>
<hr>
<h2 id="os의-race-condition-발생과-해결">OS의 Race Condition 발생과 해결</h2>
<p>운영체제는 커널 내부에서도 Race Condition이 발생할 수 있기 때문에 이를 방지하기 위한 여러 방법을 사용한다.</p>
<h3 id="1-커널-작업-중-인터럽트-발생-시-인터럽트-지연-처리">1. 커널 작업 중 인터럽트 발생 시, 인터럽트 지연 처리</h3>
<p>중요한 데이터에 접근하고 있을 때는 인터럽트를 바로 처리하지 않고 해당 작업 이후에 처리한다.</p>
<h3 id="2-프로세스가-커널-모드-수행-중인데-문맥-교환이-발생할-경우-커널-모드에서-문맥-교환을-방지함">2. 프로세스가 커널 모드 수행 중인데 문맥 교환이 발생할 경우, 커널 모드에서 문맥 교환을 방지함</h3>
<p>커널 모드에서는 커널 주소 공간의 데이터를 사용하기 때문에 race condition이 발생할 수 있어서,  </p>
<p>커널 모드에서 수행 중일 때는 CPU를 빼앗기지 않고 사용자 모드로 돌아갈 때 CPU를 반납한다.</p>
<h3 id="3-다중-cpu-환경에서의-동기화">3. 다중 CPU 환경에서의 동기화</h3>
<p>다중 CPU 환경, 즉 여러 개의 CPU가 같은 데이터에 접근하는 경우에는 데이터 락(lock)을 사용함</p>
<p>커널 내부에 있는 각 공유 데이터에 접근할 때마다 그 데이터에 lock/unlock을 걸어서 다른 접근을 차단한다.</p>
<hr>
<h2 id="크리티컬-섹션-critical-section">크리티컬 섹션 (Critical Section)</h2>
<p>두 개 이상의 프로세스나 스레드가 동시에 접근하면 안 되는 공유 자원을 사용하는 코드 영역을 의미한다.</p>
<p>공유 자원의 데이터 일관성을 보장하기 위해 한 번에 하나의 프로세스 또는 스레드만 크리티컬 섹션을 실행할 수 있도록 제어해야 한다.</p>
<p>Race Condition은 <strong>크리티컬 섹션에 대한 접근 제어가 실패했을 때</strong> 발생한다고 볼 수 있다.</p>
<hr>
<h2 id="크리티컬-섹션-문제-critical-section-problem">크리티컬 섹션 문제 (Critical Section Problem)</h2>
<p>여러 프로세스가 동시에 크리티컬 섹션에 접근하면 Race Condition이 발생할 수 있어,  </p>
<p>운영체제는 다음 세 조건을 만족하는 방법으로 크리티컬 섹션 문제를 해결한다.</p>
<h3 id="1-상호-배제-mutual-exclusion">1. 상호 배제 (Mutual Exclusion)</h3>
<p>한 번에 하나의 프로세스만 크리티컬 섹션에 진입할 수 있어야 한다.</p>
<h3 id="2-진행-조건-progress">2. 진행 조건 (Progress)</h3>
<p>크리티컬 섹션에 프로세스가 없다면 진입을 원하는 프로세스는 들어갈 수 있어야 한다.</p>
<h3 id="3-유한-대기-bounded-waiting">3. 유한 대기 (Bounded Waiting)</h3>
<p>대기 중인 프로세스가 일정한 시간 내에 크리티컬 섹션에 진입할 수 있어야 한다.</p>
<hr>
<h2 id="각-조건이-충족되지-않는-예시">각 조건이 충족되지 않는 예시</h2>
<h3 id="상호-배제-불충족">상호 배제 불충족</h3>
<ul>
<li>여러 프로세스가 동시에 공유 자원에 접근<br>→ 데이터 일관성이 흐트러지는 문제 발생</li>
</ul>
<h3 id="진행-조건-불충족">진행 조건 불충족</h3>
<ul>
<li>크리티컬 섹션에 프로세스가 없음에도 진입이 불가<br>→ 예를 들어, 두 프로세스가 대기 상태이나<br>  운영체제에서 이를 제대로 처리하지 못해 어느 쪽도 진입하지 못해 자원이 낭비됨</li>
</ul>
<h3 id="유한-대기-불충족">유한 대기 불충족</h3>
<ul>
<li>우선순위를 정하여 크리티컬 섹션에 접근하는 순서를 정하는 방식<br>→ 높은 우선순위의 프로세스가 계속 들어오면 특정 프로세스가 계속 기다려야 하는 상황 발생</li>
</ul>
<hr>
<h2 id="크리티컬-섹션-문제의-해결--세마포어">크리티컬 섹션 문제의 해결 – 세마포어</h2>
<h3 id="세마포어-semaphore란">세마포어 (Semaphore)란</h3>
<p>운영체제에서 여러 프로세스나 스레드가 공유 자원에 접근하는 것을 동기화하기 위해 사용하는 기술</p>
<p>세마포어는 <strong>정수 값</strong>을 사용하여 자원 접근 가능 여부를 제어함</p>
<hr>
<h2 id="세마포어-주요-특징">세마포어 주요 특징</h2>
<h3 id="값">값</h3>
<ul>
<li>세마포어는 정수 값을 가지며, 자원 개수를 나타냄</li>
<li>값이 0보다 크면 자원을 사용할 수 있고, 0이면 자원이 모두 사용 중임</li>
</ul>
<h3 id="작업-단위">작업 단위</h3>
<ul>
<li>P(S) 연산: 자원 획득 연산, 세마포어 값을 1 감소 (0이면 대기)</li>
<li>V(S) 연산: 자원 반납 연산, 세마포어 값을 1 증가</li>
</ul>
<h3 id="동기화-보장">동기화 보장</h3>
<ul>
<li>한 번에 하나의 프로세스만 크리티컬 섹션에 접근하도록 보장</li>
<li>자원 개수를 기준으로 동시 접근을 제한할 수 있음</li>
</ul>
<hr>
<h2 id="세마포어의-종류">세마포어의 종류</h2>
<h3 id="카운팅-세마포어-counting-semaphore">카운팅 세마포어 (Counting Semaphore)</h3>
<ul>
<li>정수 값이 0 이상을 가지며, 동시에 접근 가능한 자원의 개수를 나타냄</li>
<li>예: 프린터가 3대 있을 경우, 최대 3개의 프로세스가 동시에 프린터를 사용할 수 있음</li>
</ul>
<h3 id="이진-세마포어-binary-semaphore--뮤텍스-mutex">이진 세마포어 (Binary Semaphore) = 뮤텍스 (Mutex)</h3>
<ul>
<li>값이 0 또는 1</li>
<li>한 번에 하나의 프로세스만 접근 가능</li>
</ul>
<hr>
<h2 id="세마포어의-두-가지-대기-상태-처리-방식">세마포어의 두 가지 대기 상태 처리 방식</h2>
<h3 id="busy-wait">Busy-Wait</h3>
<ul>
<li>프로세스가 자원을 사용하기 위해 세마포어 값을 계속 확인하며 대기하는 방식</li>
<li>자원이 사용 가능해질 때까지 CPU를 사용하며 루프 반복</li>
</ul>
<h3 id="block--wakeup">Block / Wakeup</h3>
<ul>
<li>자원이 사용 중일 때 프로세스를 Blocked 상태로 전환하고<br>자원이 가용해지면 깨움(Wakeup)으로써 실행을 재개하는 방식</li>
<li>CPU를 낭비하지 않고 효율적으로 대기함</li>
</ul>
<h4 id="동작-방식">동작 방식</h4>
<ul>
<li>P(S) 호출 시, 세마포어 값이 0 미만이면 프로세스가 block() 호출<br>→ 운영체제는 이 프로세스를 세마포어의 Wait Queue에 추가하고 CPU 제어권을 빼앗음</li>
<li>V(S) 호출 시, Wait Queue에서 대기 중인 다른 프로세스를 깨워 실행 재개</li>
</ul>
<p>※ Critical section의 길이가 짧으면 block/wakeup의 오버헤드가 더 커질 수 있어서 busy wait이 더 효율적인 경우도 있다.</p>
<hr>
<h2 id="데드락-deadlock-교착-상태">데드락 (Deadlock, 교착 상태)</h2>
<p>두 개 이상의 프로세스가 서로가 소유한 자원을 요청하며 대기 상태에 빠져 더 이상 실행을 진행할 수 없는 상황</p>
<p>공유 자원이나 동기화 기법을 잘못 설계했을 때 발생함</p>
<hr>
<h2 id="데드락의-발생-조건-4가지">데드락의 발생 조건 4가지</h2>
<h3 id="1-상호-배제-mutual-exclusion-1">1. 상호 배제 (Mutual Exclusion)</h3>
<ul>
<li>자원은 한 번에 하나의 프로세스만 사용할 수 있어야 함</li>
</ul>
<h3 id="2-점유-대기-hold-and-wait">2. 점유 대기 (Hold and Wait)</h3>
<ul>
<li>프로세스가 이미 할당된 자원을 점유한 상태에서 추가로 다른 자원을 요청하며 대기함</li>
</ul>
<h3 id="3-비선점-no-preemption">3. 비선점 (No Preemption)</h3>
<ul>
<li>이미 할당된 자원은 해당 프로세스가 작업을 끝낼 때까지 강제로 뺏을 수 없음</li>
</ul>
<h3 id="4-순환-대기-circular-wait">4. 순환 대기 (Circular Wait)</h3>
<ul>
<li>프로세스들이 순환 형태로 자원을 요청하며 대기  </li>
</ul>
<p>예: 프로세스 A가 B의, B가 A의 자원을 요청</p>
<hr>
<h2 id="데드락-해결-방법">데드락 해결 방법</h2>
<h3 id="1-예방-prevention">1. 예방 (Prevention)</h3>
<p>데드락이 발생하려면 4가지 조건이 모두 충족되어야 함<br>이 중 하나 이상의 조건을 충족하지 않도록 설계하여 데드락을 <strong>원천적으로</strong> 방지</p>
<h4 id="상호-배제-방지">상호 배제 방지</h4>
<p>공유 자원을 여러 프로세스가 동시에 사용할 수 있도록 설계<br>예: 읽기 전용 데이터는 동시 접근 허용<br>제한: 쓰기 작업은 여전히 상호 배제가 필요  </p>
<h4 id="점유-대기-방지">점유 대기 방지</h4>
<p>자원을 점유한 상태에서 다른 자원을 요청하지 못하도록 설계
모든 필요한 자원을 한꺼번에 요청하도록 강제<br>단점: 자원 낭비 가능  </p>
<h4 id="비선점-방지">비선점 방지</h4>
<p>자원이 점유 중일 경우, 자원을 강제로 회수하여 다른 프로세스가 사용할 수 있도록 설정 
단점: 데이터 일관성 문제, 오버헤드  </p>
<h4 id="순환-대기-방지">순환 대기 방지</h4>
<p>자원에 번호를 부여하고 증가 순서로만 요청<br>단점: 유연성 감소  </p>
<h3 id="2-회피-avoidance">2. 회피 (Avoidance)</h3>
<p>시스템 상태를 평가하여 데드락이 발생하지 않을 경우에만 자원 할당<br>프로세스의 최대 자원 요구량을 미리 알아야 함  </p>
<p>안전 상태(Safe State) 확인 후 자원 할당
(안전 상태: 모든  프로세스가 실행을 완료할 수 있는 상태)</p>
<p><strong>동작</strong>
프로세스가 지원 요청
요청한 자원을 할당한 후의 시스템 상태를 시뮬레이션
할당 후에도 시스템이 안전 상태라면 자원을 할당, 아니라면 요청 거절</p>
<p>장점: 데드락 가능성 최소화<br>단점: 계산 비용 증가, 사전 정보 필요  </p>
<h3 id="3-탐지-및-회복-detection--recovery">3. 탐지 및 회복 (Detection &amp; Recovery)</h3>
<h4 id="탐지-detection">탐지 (Detection)</h4>
<p>데드락이 발생했는지 주기적으로 검사
자원 할당 그래프를 사용하여 데드락 상태(사이클 존재 여부)를 확인</p>
<h4 id="회복-recovery">회복 (Recovery)</h4>
<p>데드락 상태를 탐지한 후 문제를 해결하기 위한 조치를 취함
프로세스 종료 또는 자원 선점  </p>
<p>프로세스 종료: 데드락을 유발하는 프로세스를 하나 이상 종료
자원 선점: 특정 프로세스에서 자원을 강제로 회수</p>
<p>단점: 데이터 일관성 문제 발생 가능</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CPU 스케줄링]]></title>
            <link>https://velog.io/@siha_014/CPU-%EC%8A%A4%EC%BC%80%EC%A4%84%EB%A7%81</link>
            <guid>https://velog.io/@siha_014/CPU-%EC%8A%A4%EC%BC%80%EC%A4%84%EB%A7%81</guid>
            <pubDate>Wed, 07 Jan 2026 08:00:06 GMT</pubDate>
            <description><![CDATA[<h1 id="cpu-스케줄링">CPU 스케줄링</h1>
<p>: 여러 프로세스가 CPU를 기다리는 상황에서 CPU를 어떤 프로세스에 할당할지 결정하는 과정
스케줄링 알고리즘을 통해 프로세스 실행 순서를 결정한다.</p>
<h2 id="cpu-스케줄링-성능-척도">CPU 스케줄링 성능 척도</h2>
<p>; 성능 평가 지표</p>
<h4 id="cpu-이용률-cpu-utilization">CPU 이용률 (CPU Utilization)</h4>
<p>전체 시간 중 CPU가 놀지 않고 일한 시간 비율.
높을수록 CPU를 가용할 수 있는 시간 대비 CPU가 일한 시간이 높은 것이므로, 높을수록 좋음</p>
<h4 id="처리량-throughput">처리량 (Throughput)</h4>
<p>주어진 시간 동안 처리되는 프로세스 수.
클수록 좋음</p>
<h4 id="대기-시간-waiting-time">대기 시간 (Waiting Time)</h4>
<p>Ready 상태에서 CPU를 기다린 총 시간
Ready Queue에 머문 시간
짧을수록 좋음</p>
<h4 id="응답-시간-response-time">응답 시간 (Response Time)</h4>
<p>Ready 상태의 프로세스에게 최초로 CPU가 할당되기까지 걸린 시간.
즉, 얼마나 빨리 시작할 수 있는지</p>
<h4 id="소요-시간-turnaround-time">소요 시간 (Turnaround time)</h4>
<p>프로세스가 CPU를 요청한 시점으로부터 원하는 만큼의 CPU를 모두 사용하는 데 걸린 시간
짧을 수록 좋음</p>
<blockquote>
<p>시스템 입장에서의 성능 척도</p>
</blockquote>
<ul>
<li>CPU 하나 가지고 최대한 많은 일을 시킬 수 있으면 성능이 좋음</li>
<li>주요 지표: CPU 이용률, 처리량</li>
</ul>
<blockquote>
<p>프로그램 입장에서의 성능 척도</p>
</blockquote>
<ul>
<li>프로그램이 최대한 빨리 끝날 수 있으면 성능이 좋음</li>
<li>주요 지표: 대기 시간, 응답 시간, 소요 시간</li>
</ul>
<h2 id="cpu-스케줄링-알고리즘">CPU 스케줄링 알고리즘</h2>
<h3 id="선점형-스케줄링과-비선점형-스케줄링">선점형 스케줄링과 비선점형 스케줄링</h3>
<p>선점형 스케줄링은 CPU를 강제로 빼앗기지만,
비선점형 스케줄링은 CPU를 강제로 빼앗기지 않음.
즉, 비선점형은 모든 실행을 끝내야지만 다음 프로세스를 실행할 수 있다.</p>
<h4 id="fcfs-first-come-first-serve">FCFS (First-Come First-Serve)</h4>
<ul>
<li>먼저 온 프로세스부터 CPU를 할당, 비선점형 스케줄링</li>
<li>단점: 소요 시간이 긴 프로세스가 먼저 도착할 경우, 전체적으로 프로세스들의 효율성을 낮춤</li>
</ul>
<h4 id="sjf-shortest-job-first">SJF (Shortest-Job-First)</h4>
<blockquote>
<p><strong>CPU Burst</strong>: 프로세스가 CPU를 사용해 계산이나 데이터 처리 작업을 수행하는 시간
<strong>Starvation</strong>: 우선순위가 낮은 프로세스가 장시간 동안 자원을 할당받지 못하는 상태</p>
</blockquote>
<ul>
<li>CPU Burst가 짧은 프로세스에게 먼저 CPU를 할당, 비선점형 스케줄링
CPU를 적게 사용하는 프로세스 먼저 실행</li>
<li>단점: CPU Burst가 긴 프로세스는 오랜 시간 CPU를 할당받지 못하는 starvation 문제가 발생할 수 있음</li>
</ul>
<h4 id="srtf-shortest-remaining-time-first">SRTF (Shortest-Remaining-Time-First)</h4>
<ul>
<li>SJF와 유사하나 중간에 새로운 프로세스가 도착하면 새로 스케줄링, 선점형 스케줄링</li>
<li>현재 실행 중인 프로세스의 남은 CPU Burst 시간보다 더 짧은 CPU Burst 시간을 가지는 프로세스가 도착하면 CPU를 빼앗김</li>
</ul>
<h4 id="priority-scheduling">Priority Scheduling</h4>
<ul>
<li>우선순위대로 CPU 할당</li>
<li>선점형 스케줄링 방식에서는 더 높은 우선 순위의 프로세스가 도착하면 그 프로세스에게 CPU를 빼앗김</li>
<li>비선점형 스케줄링 방식에서는 더 높은 우선 순위의 프로세스가 도착하더라도 그 프로세스는 우선 ready queue에 위치됨</li>
<li>이 방식에서도 starvation 문제가 발생하여 aging 기법을 사용</li>
<li>aging은 오래 기다린 프로세스에게 우선순위를 높여주는 것</li>
</ul>
<h4 id="round-robin">Round Robin</h4>
<ul>
<li>가장 현대적인 방식, 각 프로세스에게 동일한 크기의 CPU 할당 시간을 줌</li>
<li>할당 시간이 지난 프로세스는 ready queue의 제일 뒤에 가서 다시 CPU를 기다림</li>
<li>장점: 가장 빠른 응답 시간을 기대할 수 있음</li>
<li>주의: 할당 시간이 너무 큰 경우, FCFS처럼 동작해버리고,
너무 짧은 경우, 문맥 교환 오버헤드(Context switching overhead)가 커지기 때문에 적절한 할당시간을 설정해야 함</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>