<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>lucas_choi.log</title>
        <link>https://velog.io/</link>
        <description>bla</description>
        <lastBuildDate>Sun, 19 Jan 2025 13:50:32 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. lucas_choi.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/lucas_choi" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[민감한 파일 처리 규칙(feat. 맥에 수상한 접근...)]]></title>
            <link>https://velog.io/@lucas_choi/%EB%AF%BC%EA%B0%90%ED%95%9C-%ED%8C%8C%EC%9D%BC-%EC%B2%98%EB%A6%AC-%EA%B7%9C%EC%B9%99feat.-%EB%A7%A5%EC%97%90-%EC%88%98%EC%83%81%ED%95%9C-%EC%A0%91%EA%B7%BC</link>
            <guid>https://velog.io/@lucas_choi/%EB%AF%BC%EA%B0%90%ED%95%9C-%ED%8C%8C%EC%9D%BC-%EC%B2%98%EB%A6%AC-%EA%B7%9C%EC%B9%99feat.-%EB%A7%A5%EC%97%90-%EC%88%98%EC%83%81%ED%95%9C-%EC%A0%91%EA%B7%BC</guid>
            <pubDate>Sun, 19 Jan 2025 13:50:32 GMT</pubDate>
            <description><![CDATA[<p>맥 서버를 운영하기 시작한지 1주일쯤? 됐는데....</p>
<p>웹 페이지를 올리자마자 nginx 로그에서 수상한 접근이 포착됐다.</p>
<pre><code>&quot;GET /.env HTTP/1.1&quot; 200 853 &quot;-&quot; &quot;Mozilla/5.0 (X11; Linux x86_64; rv:83.0) Gecko/20100101 Firefox/83.0&quot;</code></pre><p>이런 로그가 있길래 GPT 선생한테 물어봤더니...
<img src="https://velog.velcdn.com/images/lucas_choi/post/1c10ce53-1a3c-4446-979e-c0020ae57ab6/image.png" alt=""></p>
<p>헉!!!!!</p>
<p>해석에 따르면 누군가가 .env파일을 노리고 접속을 했다는 것!! 그리고 이 파일에는 보통 어플리케이션의 민감한 정보(데이터베이스 비밀번호, API 키 등)가 저장되어있다. 그리고 상태코드 200은 해당 요청이 아~주 정상적으로 서버에서 접근자에게 반환이 되었다는 것... 다행히도 해당 서비스는 아주 기초적인 서비스라 별 내용이 안 들어있었지만 혹시라도 중요한 정보가 있었다면...ㄷㄷㄷㄷ</p>
<p>이를 방지하기 위해서는 nginx에서 해당 리소스에 접근하지 못하도록 설정을 할 수 있다.</p>
<p>nginx 설정에서 아래와 같은 규칙을 추가하면 .env 파일에 접근할 수 없다.</p>
<pre><code>location ~ /\.env {
    deny all;    # 접근을 차단
    return 404;
}</code></pre><p>deny all만 적용해도 되지만... 이는 서버가 파일에 접근할 수 없음을 알려주기 때문에, 공격자 입장에서는 차단된 파일이 존재한다는 힌트를 얻을 수 있다. 이 때, 404로 응답하면 파일이 존재하지 않는 것처럼 보이게 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Mac Server에서 포트 개방하기]]></title>
            <link>https://velog.io/@lucas_choi/Mac-Server%EC%97%90%EC%84%9C-%ED%8F%AC%ED%8A%B8-%EA%B0%9C%EB%B0%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@lucas_choi/Mac-Server%EC%97%90%EC%84%9C-%ED%8F%AC%ED%8A%B8-%EA%B0%9C%EB%B0%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 14 Jan 2025 09:22:50 GMT</pubDate>
            <description><![CDATA[<pre><code># pf.conf: 방화벽 설정을 열어서 포트 추가
sudo vim /etc/pf.conf

# [pf.conf]
pass in inet proto tcp from any to any port 11111 # 11111 포트 추가
pass in inet proto tcp from any to any port 22222 # 22222 포트 추가

pass out all

# 수정한 규칙 활성화
sudo pfctl -f /etc/pf.conf

# 방화벽 활성화
sudo pfctl -e

# 설정한 규칙 확인
sudo pfctl -s rules</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Hexagonal 아키텍처]]></title>
            <link>https://velog.io/@lucas_choi/Hexagonal-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98</link>
            <guid>https://velog.io/@lucas_choi/Hexagonal-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98</guid>
            <pubDate>Fri, 15 Nov 2024 19:31:49 GMT</pubDate>
            <description><![CDATA[<pre><code>project # 최상위 프로젝트
    └- service1    # 서비스
    |    ├- src.main
    |   |      └- java
    |   |       └- package    # 각 서비스 별 최상위 패키지
    |   |           └- adapter            # adapter는 외부 시스템과의 상호작용을 담당
    |   |           |    ├- in.web
    |    |            |    |    ├- Rest Controller
    |    |            |    |    └- Request
    |   |           |   └- out.persistence
    |   |           |        ├- Entity
    |   |           |        ├- Mapper
    |   |           |        ├- Adapter
    |   |           |        └- Repository
    |   |           ├- application        # 비즈니스 로직
    |   |            |    ├- port
    |   |            |    |    ├- in
    |   |           |    |    |    ├- UseCase
    |   |           |    |    |    └- Command
    |   |            |    |    └- out
    |   |            |    |        └- Port
    |   |            |    └- service        # service는 실제 비즈니스 로직의 구현체
    |   |            |        └- Service
    |   |           └- domain            # domain은 비즈니스 로직과 핵심 데이터 구조를 저장
    |   |               └- Domain
    |   |
    |    └- build.gradle
    |   
    ├- service2
    |
    └- service3
    ...
</code></pre><p><img src="https://velog.velcdn.com/images/lucas_choi/post/26c29116-76e9-4b37-bfc6-616638404053/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[MSA의 핵심 원칙]]></title>
            <link>https://velog.io/@lucas_choi/MSA%EC%9D%98-%ED%95%B5%EC%8B%AC-%EC%9B%90%EC%B9%99</link>
            <guid>https://velog.io/@lucas_choi/MSA%EC%9D%98-%ED%95%B5%EC%8B%AC-%EC%9B%90%EC%B9%99</guid>
            <pubDate>Mon, 11 Nov 2024 09:18:00 GMT</pubDate>
            <description><![CDATA[<h2 id="msa라는-개념과-관련해서-많은-이론들을-만들어-낸-마틴-파울러martin-fowler가-정의한-msa-특징">MSA라는 개념과 관련해서 많은 이론들을 만들어 낸 마틴 파울러(Martin Fowler)가 정의한 MSA 특징</h2>
<h4 id="componentization-via-services-서비스를-통한-컴포넌트화">Componentization via Services: 서비스를 통한 컴포넌트화</h4>
<p>모놀리틱 시스템에서는 기능을 코드 모듈이나 라이브러리로 구분하지만 MSA에서는 기능을 독립된 서비스로 분리한다. 각 서비스가 특정 기능을 책임지고, API를 통해 다른 서비스와 통신한다. 이를 통해 서비스 단위로 시스템을 컴포넌트화하여 유지보수와 확장성을 높이고, 개별적으로 배포와 개발이 가능하게 한다.</p>
<h4 id="⭐️⭐️⭐️organized-around-business-capabilities-비즈니스-기능-중심의-구조">⭐️⭐️⭐️Organized around Business Capabilities: 비즈니스 기능 중심의 구조</h4>
<pre><code>Conway&#39;s Law
    &quot;시스템이 설계되면, 그 시스템의 구조는 설계한 조직의 구조와 유사해진다.&quot;
Business Capabilities란
    조직이 얼마나 빠르고 유연하게 변화에 대응할 수 있는지에 대한 능력</code></pre><p>빠른 대처가 필요한 비즈니스 모델을 가진다는 의미는 빠른 대처가 가능한 MSA를 가진다는 것과 유사한 의미이고, 빠른 대처가 가능한 MSA가 되려면 이와 유사한 조직 구조가 생긴다. 이렇게 구성된 조직은 빠른 대처가 가능하다.</p>
<h4 id="products-not-projects-프로젝트가-아닌-제품">Products not Projects: 프로젝트가 아닌 제품</h4>
<p>프로젝트는 일회성 - 프로젝트를 달성하게 되면 유지보수 조직으로 개발의 주도권이 넘어간다.
MSA는 각 서비스를 프로젝트가 아닌 지속적으로 발전하고 유지되는 제품으로 간주하기 때문에 기능 업데이트와 유지보수가 쉽게 이루어질 수 있는 구조이다.</p>
<h4 id="smart-endpoints-and-dumb-pipes-스마트-엔드포인트와-단순한-파이프라인">Smart endpoints and dumb pipes: 스마트 엔드포인트와 단순한 파이프라인</h4>
<p>Smart endpoints: 각 서비스의 엔드포인트(집입점)가 데이터를 받고 비즈니즈 로직을 수행하며, 이를 처리하는 똑똑한 역할을 한다.
dumb pipes: 파이프라인은 엔드포인트들 간에 데이터를 전달하는 데만 집중하는 단순한 통로 역할을 한다.
이런 방식 덕분에 서비스들 간 통신은 최소한의 역할만 담당하고, 실제 기능은 각 서비스 내부에서 처리되기 때문에 시스템이 더 단순하고 유지보수하기 쉬워진다.</p>
<h4 id="decentralized-data-managementgovernance-분산-데이터-관리-및-거버넌스">Decentralized Data Management/Governance: 분산 데이터 관리 및 거버넌스</h4>
<p>Decentralized Data Management: 데이터 베이스의 분리. 모노리시 방식에서는 일반적으로 하나의 DB를 사용하기 때문에 스케일 업이나 분리하기 어려웠으나, 데이터 베이스의 분리를 통해 데이터의 유연성과 탄력성을 확보할 수 있다.
Decentralized Governance: 시스템의 관리와 결정 권한을 한 곳에 집중시키지 않고, 각 서비스 또는 팀이 독립적으로 필요한 결정과 관리를 수행할 수 있도록 분산시킨다. MSA에서는 각 서비스가 독립적으로 운영되고, 각기 다른 요구 사항이나 변화에 맞춰 빠르게 대응할 수 있어야 하기 때문에 이 개념이 중요시된다.</p>
<h4 id="infrastructure-automation-인프라-자동화">Infrastructure Automation: 인프라 자동화</h4>
<p>MSA에서는 배포 빈도가 높고 서비스 개수가 많기 때문에 인프라 자동화가 필수적이다.</p>
<h4 id="design-for-failure-장애를-고려한-설계">Design for Failure: 장애를 고려한 설계</h4>
<p>MSA는 장애가 발생할 가능성을 염두에 두고 설계되어야 한다. 서비스 간 독립성을 보장하고, 오류를 감지하고 대응할 수 있는 매커니즘을 포함해야 한다. 예를 들어, 하나의 서비스가 다운되더라도 전체 시스템이 정상적으로 작동할 수 있도록 장애를 격리하거나 재시도 로직을 포함시킨다. 이를 통해 시스템의 안정성을 높일 수 있다.</p>
<h4 id="evolutionary-design-진화적-설계">Evolutionary Design: 진화적 설계</h4>
<p>마이크로서비스는 점진적이고 유연한 설계 방식을 지향한다. 처음부터 완벽한 시스템을 만드는 대신, 시간이 지나면서 새로운 요구 사항과 기능을 반영해 시스템을 점진적으로 개선해 나간다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[
GitHub Actions로 CI/CD 구축하기]]></title>
            <link>https://velog.io/@lucas_choi/GitHub-Actions%EB%A1%9C-CICD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@lucas_choi/GitHub-Actions%EB%A1%9C-CICD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 12 Oct 2024 12:09:15 GMT</pubDate>
            <description><![CDATA[<p>GitHub Actions는 GitHub가 제공하는 CI/CD 도구로 소프트웨어 개발 워크플로우를 자동화할 수 있도록 도와준다.</p>
<h3 id="주요-개념">주요 개념</h3>
<h4 id="1-워크플로우workflow">1. 워크플로우(Workflow)</h4>
<ul>
<li>Workflow는 자동화하고자 하는 일련의 작업을 정의하는 것으로, 코드가 어떤 branch에 작업이 일어날 때, 빌드, 테스트, 배포 등의 작업이 자동으로 수행될 수 있도록 설정할 수 있다.</li>
<li>주로 github repository 내에서 설정파일(.yml)을 작성해서 사용한다.</li>
</ul>
<h4 id="2-이벤트event">2. 이벤트(Event)</h4>
<ul>
<li>이벤트는 워크플로우가 실행되는 조건이다. 예를 들어, 코드 push, pull request, schedule, repo 생성 등이 이벤트가 될 수 있다. 이런 이벤트가 발생하면 GitHub Actions가 자동으로 설정된 워크플로우를 실행하게 된다.</li>
</ul>
<h4 id="3-잡job">3. 잡(Job)</h4>
<ul>
<li>잡은 워크플로우 내에서 개별적으로 실행되는 작업의 단위이다. 각 잡은 독립된 가상 환경에서 실행된다. 여러 잡을 동시에 수행하거나, 여러 잡들을 순차적으로 수행되도록 할 수 있다.</li>
</ul>
<h4 id="4-스텝step">4. 스텝(Step)</h4>
<ul>
<li>스텝은 잡 안에서 실행되는 개별 작업이다. 각 스텝은 명령어를 실행하도록 하거나, github에서 제공하는 액션을 호출할 수 있다.</li>
</ul>
<h4 id="5-액션action">5. 액션(Action)</h4>
<ul>
<li>액션은 스텝에서 사용할 수 있는 재사용 가능한 명령어 모음으로 github에서 기본적으로 제공하는 액션이나, 다른 사람이 만들어 놓은 액션을 가져다 사용할 수 있다.</li>
</ul>
<h3 id="workflowyml로-프로젝트를-개인-서버에-배포하기">workflow.yml로 프로젝트를 개인 서버에 배포하기</h3>
<pre><code>name: Docker Image CI        # workflow 이름을 지어준다.

on:
  push:
    branches:
      - &quot;main&quot;                # main 브랜치에 push 될 때 해당 workflow를 실행한다.</code></pre><pre><code>jobs:    # job을 수행한다.</code></pre><pre><code>  build:    # 프로젝트를 빌드(CI)
    runs-on: ubuntu-latest    # build 할 가상환경을 지정해 준다. 이후의 작업은 이때 설정한 가상환경에서 수행된다.

    steps:
      - name: Checkout        # checkout 액션을 통해 가상환경에서 레포지토리를 다운받은 폴더로 이동
        uses: actions/checkout@v4.2.1
        with:
          ref: ${{ github.ref }}

      - name: Set up JDK 17    # java 17 버전을 설치한다.
        uses: actions/setup-java@v4.4.0
        with:
          java-version: &#39;17&#39;
          distribution: &#39;temurin&#39;

      - name: Grant permission for gradlew    # gradlew 파일에 실행 권한을 부여하여 다음 단계에서 gradlew가 빌드 명령을 수행할 수 있도록 한다.
        run: chmod +x ./gradlew

      - name: Build with Gradle    # 프로젝트를 clean 상태로 정리한 후, test를 생략하고 빌드를 수행.
        run: ./gradlew clean build -x test

      - name: Docker build and publish    # docker 이미지를 생성하고 docker hub에 이미지를 업로드한다.
        run: |
          echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
          docker build -t ${{ secrets.DOCKER_USERNAME }}/${{ vars.APP_NAME }}:${{ github.ref_name }} .
          docker push ${{ secrets.DOCKER_USERNAME }}/${{ vars.APP_NAME }}:${{ github.ref_name }}</code></pre><p>위 작업이 수행되면 docker hub에 내가 빌드한 프로젝트의 docker image가 올라간 것을 확인할 수 있다.</p>
<pre><code>  deploy:    # build 된 docker 이미지를 서버에서 배포(CD)
    needs: build    # build가 정상적으로 수행된 경우에만 배포가 되도록 한다.
    runs-on: ubuntu-latest
    if: github.ref == &#39;refs/heads/main&#39;
    steps:
      - name: My Server Login and Docker Image pull and run    # 내 서버에 접속해서 docker 이미지를 pull 하고 이를 run 하여 프로젝트 container를 생성/실행한다.
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USERNAME }}
          password: ${{ secrets.SERVER_PASSWORD }}
          port: ${{ secrets.SERVER_PORT }}
          script: |
            echo ${{ secrets.SERVER_PASSWORD }} | sudo -S docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
            echo ${{ secrets.SERVER_PASSWORD }} | sudo -S docker stop ${{ vars.APP_NAME }}
            echo ${{ secrets.SERVER_PASSWORD }} | sudo -S docker rm -f ${{ vars.APP_NAME }}
            echo ${{ secrets.SERVER_PASSWORD }} | sudo -S docker network create ${{ vars.APP_NAME }}_net || true  # 네트워크 생성
            echo ${{ secrets.SERVER_PASSWORD }} | sudo -S docker pull ${{ secrets.DOCKER_USERNAME }}/${{ vars.APP_NAME }}:main
            echo ${{ secrets.SERVER_PASSWORD }} | sudo -S docker run -d --name ${{ vars.APP_NAME }} --network ${{ vars.APP_NAME }}_net -p ${{ secrets.SERVER_PORT }}:8080 ${{ secrets.DOCKER_USERNAME }}/${{ vars.APP_NAME }}:main  # 네트워크 설정
            echo ${{ secrets.SERVER_PASSWORD }} | sudo -S docker image prune -f</code></pre><p>내 서버에서 docker ps 명령어로 작업 중인 프로세스를 확인하면 내 프로젝트 docker container가 생성/실행되는 것을 확인할 수 있다.</p>
<p>이 과정에서 서버 정보와 같이 민감한 정보들은 github repository &gt; settings &gt; Secrets and variables &gt; actions에서 저장할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[docker로 mysql 올려서 사용하기]]></title>
            <link>https://velog.io/@lucas_choi/docker%EB%A1%9C-mysql-%EC%98%AC%EB%A0%A4%EC%84%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@lucas_choi/docker%EB%A1%9C-mysql-%EC%98%AC%EB%A0%A4%EC%84%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 11 Oct 2024 05:05:04 GMT</pubDate>
            <description><![CDATA[<p>mysql을 docker container로 올려서 사용할 것이다.</p>
<ol>
<li><p>mysql 이미지 다운로드</p>
<pre><code># docker pull mysql</code></pre></li>
<li><p>mysql 이미지 확인</p>
<pre><code># docker images</code></pre><p><img src="https://velog.velcdn.com/images/lucas_choi/post/039fd961-9dfe-44af-91c5-de27d648ebcc/image.png" alt="">
위 이미지와 같이 mysql 이미지가 정상적으로 받아와 졌는지 확인</p>
</li>
<li><p>mysql docker container 생성 및 실행</p>
<pre><code># docker run --name [mysql 컨테이너명] -e MYSQL_ROOT_PASSWORD=[mysql 비밀번호] -d -p [원하는 mysql 포트]:3306 mysql
// 우분투에서
// &#39;!!&#39;는 가장 마지막으로 실행한 명령어를 실행,
// &#39;!&#39;는 이전에 실행한 특정 명령어를 찾고, 가장 최근에 해당 명령어로 시작한 명령을 실행하는 명령어이다.
</code></pre></li>
</ol>
<p>// 그래서 이 문자들을 넣으면 컨테이너가 설정과 다르게 실행되거나 원하는 설정이 변경되어 실행될 수 있다.
// 따라서, 이런 특수문자를 넣을 때 &#39;비밀번호&#39;와 같이 입력하는 것이 좋다.</p>
<pre><code>
4. docker container 확인
![](https://velog.velcdn.com/images/lucas_choi/post/e19b07b7-fff0-4c26-b82f-a7f2093d3614/image.png)

5. mysql container 접속 확인</code></pre><h1 id="docker--exec--it-mysql-attendance-management-bash">docker  exec -it mysql-attendance-management bash</h1>
<p>bash # mysql -u root -p</p>
<p>```
<img src="https://velog.velcdn.com/images/lucas_choi/post/f8485182-1cf2-41ee-8a77-2512029e6d8b/image.png" alt="">
위와 같이 mysql에 접속되면 mysql 환경 구축에 성공한 것이다!!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[마이크로 서비스란?]]></title>
            <link>https://velog.io/@lucas_choi/%EB%A7%88%EC%9D%B4%ED%81%AC%EB%A1%9C-%EC%84%9C%EB%B9%84%EC%8A%A4%EB%9E%80</link>
            <guid>https://velog.io/@lucas_choi/%EB%A7%88%EC%9D%B4%ED%81%AC%EB%A1%9C-%EC%84%9C%EB%B9%84%EC%8A%A4%EB%9E%80</guid>
            <pubDate>Wed, 09 Oct 2024 14:01:50 GMT</pubDate>
            <description><![CDATA[<ol>
<li><p>모노리스(monolith) 구조란?
마이크로 서비스 이전의 전통적인 시스템 구조인 모노리스 구조에 대해 알아보자.
모노리스 구조는 시스템의 모든 기능이 하나의 애플리케이션에 통합된 형태이다.</p>
<ul>
<li><p>장점</p>
<ul>
<li>간단한 개발 및 배포: 모든 코드가 한 덩어리에 구현되어 있으므로 개발하고 배포하기가 쉽다.</li>
<li>통합적인 테스트: 모든 기능이 하나의 프로그램 내에 존재하므로 테스트를 하거나 서비스 간의 통신 문제나 데이터 일관성 문제 같은 것에 신경 쓸 일이 적다.</li>
<li>성능 최적화: 서비스 간의 통신이 네트워크를 통해 이뤄지는 것이 아니라 프로그램 내부적으로 처리된다. 이 덕분에 속도도 더 빠르고 성능 최적화가 쉽다. 또한, 모든 기능이 한 프로그램 내에서 긴밀하게 연결되어 있기 때문에 성능 문제가 덜 발생한다.</li>
</ul>
</li>
<li><p>단점</p>
<ul>
<li>확장성의 한계: 프로그램이 한 덩어리로 이루어져 있어서, 특정 기능만 별도로 확장하기가 어렵다.</li>
<li>유지 보수의 어려움: 프로그램이 커질수록 코드도 복잡해지고, 새로운 기능을 추가하거나 수정하기 어렵다. 특히, 팀 규모가 커지게 되면 여러 사람이 동시에 같은 코드를 건드리면서 충돌이 발생할 가능성이 증가한다.</li>
<li>안정성 문제: 하나의 기능에 문제가 생기면 이로 인해 전체 애플리케이션이 영향을 받을 수 있다.</li>
</ul>
</li>
</ul>
</li>
</ol>
<p>모노리스 구조가 사용되는 경우는 보통 작은 규모의 프로젝트나 초기 단계에서 유리하다. 이 경우, 시스템이 복잡하지도 않고 기능이 많지 않기 때문에 관리도 편하고, 배포하기도 쉽다. 또한, 기능이 고정되어 있거나, 확장이 필요 없는 단순한 시스템의 경우에는 오히려 모노리스가 효율적이다. 이 외에도 클라우드 사용이 어렵거나 금융 등 보안과 안정성이 최고로 중요한 경우 모노리스를 사용하는 것이 더 좋을 수 있다.</p>
<ol start="2">
<li><p>마이크로 서비스(micro service)란?
마이크로서비스는 큰 시스템을 작은 서비스들로 나눠 각각 독립적으로 운영하는 방식이다.
ex) 쇼핑몰 시스템에서 사용자 관리, 상품 관리, 결제 등과 같은 기능이 있다고 하면, 마이크로서비스에서는 이걸 각각의 서비스로 운영한다.
각 마이크로서비스는 독립적이고 다른 서비스의 영향을 받지 않으며, 서로 자율적으로 움직일 수 있는 구조이다. 이런 이유로, 마이크로 서비스는 대규모 시스템에 적합하다. 기능이 많고, 시스템이 커질수록 서비스들을 분리해서 관리하는 게 더 유리하기 때문이다.</p>
</li>
<li><p>마이크로 서비스의 특징</p>
<ul>
<li>독립적인 배포: 각 서비스가 독립적이라, 어떤 서비스에 수정이 필요하면 해당 서비스만 배포하면 된다.</li>
<li>분산된 데이터 관리: 각 서비스는 자신의 데이터만 관리한다. 따라서 다른 서비스가 관리하는 데이터에 직접 접근할 수 없고, 필요한 경우에는 요청을 보내 데이터를 직접 받아와야 한다. 이 덕분에 보안적으로도 안전하고, 각 서비스가 자율적으로 데이터를 관리할 수 있다.</li>
<li>유연한 기술 사용: 각 서비스가 독립적이기 때문에 서비스마다 각기 다른 프로그래밍 언어나 기술을 사용해도 된다.</li>
<li>비즈니스 로직의 재사용 지양: 서비스 간 결합도를 낮추는 것을 목표로 한다. 이를 통해 애자일한 대응이 가능하다.(유동적인 개발)</li>
</ul>
</li>
<li><p>마이크로 서비스의 장점</p>
<ul>
<li>확장성: 특정 서비스에만 수요가 많아지면, 모든 서비스들을 확장할 필요 없이 해당 서비스만 확장할 수 있다.</li>
<li>빠른 개발 및 배포: 마이크로 서비스는 작은 단위로 이루어져 있어서, 새로운 기능을 추가하거나, 기존 기능을 업데이트할 때 각 서비스 단위로 빠르게 작업할 수 있다. 한 팀이 전체 시스템을 관리하는 대신, 여러 팀이 각자 담당하는 서비스를 관리한다.</li>
<li>안정성: 특정 서비스에 문제가 생기더라도 다른 서비스에는 영향을 미치지 않는다. 이 덕분에 안정적인 서비스 운영이 가능하다.</li>
</ul>
</li>
<li><p>마이크로 서비스의 단점</p>
<ul>
<li>복잡한 구조: 각 서비스가 독립적이라, 서비스 간 통신이나 데이터 일관성 문제를 신경 써야 한다. 이 때문에, 서로 다른 서비스를 연결하고 관리하는 작업에서 더 많은 시간이 소요된다.</li>
<li>통신 비용 증가: 각 서비스 간의 데이터 송수신 과정에서 네트워크를 통해 통신하기 때문에 성능 저하가 발생할 수 있다. 특히, 서비스가 많아지면 이런 통신 비용이 상당히 커질 수 있다.</li>
<li>데이터 일관성 문제: 서비스들이 각자 데이터베이스를 가지고 있으므로, 데이터를 일관되게 관리하는 것이 어렵다.</li>
</ul>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[가비아에서 도메인 구매하기]]></title>
            <link>https://velog.io/@lucas_choi/%EA%B0%80%EB%B9%84%EC%95%84%EC%97%90%EC%84%9C-%EB%8F%84%EB%A9%94%EC%9D%B8-%EA%B5%AC%EB%A7%A4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@lucas_choi/%EA%B0%80%EB%B9%84%EC%95%84%EC%97%90%EC%84%9C-%EB%8F%84%EB%A9%94%EC%9D%B8-%EA%B5%AC%EB%A7%A4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 08 Oct 2024 14:59:29 GMT</pubDate>
            <description><![CDATA[<p>맥북을 사면서도 비싼 돈 주고 맥 사면 돈 아까워서라도 뭐든 하겠지? 하고 합리화했던 나였기에...(근데 뭔가를 하기는 했음ㅋㅋㅋ) 이번에도 뭐라도 하겠지... 하면서 도메인을 구매해 본다^^</p>
<p>일단 난 가비아에서 했음... 다른데도 있는 거 같은데 제일 유명?한 거 같아서 여기서 해봤다.</p>
<ol>
<li><p>원하는 도메인을 선택
<img src="https://velog.velcdn.com/images/lucas_choi/post/0ae45e67-abc7-449b-b3d2-4de833a5fbf3/image.png" alt=""></p>
</li>
<li><p>도메인 신청
<img src="https://velog.velcdn.com/images/lucas_choi/post/7e7444e0-1019-4567-b5b3-f974e36852c6/image.png" alt=""></p>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/lucas_choi/post/a1651af9-4f86-4768-ba20-ec236092a206/image.png" alt=""></p>
<p>결제하면 끝!!! 이제 나도 도메인이 있으니 뭐라도 하겠지? ㅎ</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[내 웹 호스팅 일지]]></title>
            <link>https://velog.io/@lucas_choi/%EB%82%B4-%EC%9B%B9%ED%8E%98%EC%9D%B4%EC%A7%80-%ED%98%B8%EC%8A%A4%ED%8C%85</link>
            <guid>https://velog.io/@lucas_choi/%EB%82%B4-%EC%9B%B9%ED%8E%98%EC%9D%B4%EC%A7%80-%ED%98%B8%EC%8A%A4%ED%8C%85</guid>
            <pubDate>Tue, 08 Oct 2024 14:55:19 GMT</pubDate>
            <description><![CDATA[<p>이 프로젝트는 단순히 나의!!! 나에 의한!!! 나를 위한!!! 페이지를 갖고 싶어서 하는 프로젝트입니다~^^</p>
<p>2024.10.09
웹 개발을 하면서 뭔가 나만의 페이지를 갖고 싶어졌다. 그래서 도메인부터 무작정 구매하면 뭐라도 하겠지 싶어 바로 구매해 버렸다...
<a href="https://velog.io/@lucas_choi/%EA%B0%80%EB%B9%84%EC%95%84%EC%97%90%EC%84%9C-%EB%8F%84%EB%A9%94%EC%9D%B8-%EA%B5%AC%EB%A7%A4%ED%95%98%EA%B8%B0">가비아에서 도메인 구매하기</a></p>
<p>2024.10.10
이 프로젝트는 MSA(Micro Service Architecture)로 구현할 예정이다.
<a href="https://velog.io/@lucas_choi/%EB%A7%88%EC%9D%B4%ED%81%AC%EB%A1%9C-%EC%84%9C%EB%B9%84%EC%8A%A4%EB%9E%80">마이크로 서비스란?</a></p>
<p>2024.10.11 ~ 2024.10.12
내 페이지의 시작인 main페이지를 간단하게 만든 후, GitHub Actions를 활용해 빌드하고, 이를 내 서버에서 배포했다.
<a href="https://velog.io/@lucas_choi/GitHub-Actions%EB%A1%9C-CICD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0">GitHub Actions로 CI/CD 구축하기</a></p>
<p>2024.10.12 ~ 2024.10.13
docker container들을 간편하게 관리할 수 있도록 한 docker compose와 nginx를 이용하여 기본 페이지를 배포했다.... 드디어... 내가 만든 결과가 쬐에에<del>~</del>금 보이기 시작했다!!!!🥳🥳🥳
<a href="https://velog.io/@lucas_choi/docker-compose-nginx-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EC%8B%A4%ED%96%89%ED%95%B4%EC%84%9C-%ED%98%B8%EC%8A%A4%ED%8C%85%ED%95%98%EA%B8%B0">docker compose, nginx 컨테이너 실행해서 호스팅하기</a></p>
<p align="center"><img src="https://velog.velcdn.com/images/lucas_choi/post/175a446c-50b1-4ce1-afa4-a32f2218186e/image.png" width="70%"></p>

<p>2024.10.14 ~ 2024.10.15
웹에서 404 에러과 같은 에러가 발생했을 때, 내가 만든 에러 페이지로 넘어가게 하려고 하는데... 왜 에러 페이지에 css에 적용이 안되는 걸까....ㅠㅠㅠㅠ</p>
<p>2024.10.20 ~ 2024.10.27
Nginx에서 https 설정을 하기 위해... 이렇게 오래 걸리다니... 물론 하루에 얼마 안 하긴 했지만...
그래도 이번 기회에 docker compose, nginx 설정법에 익숙해지고 나름 많이 배웠으니깐 그걸로 위안을 삼아야지ㅎ
<a href="https://velog.io/@lucas_choi/docker-nginx%EC%97%90%EC%84%9C-https-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0">nginx에서 https 설정하기</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[docker container(ubuntu)에서 docker 사용하는 법 - 호스트 docker 소켓을 컨테이너에 연결]]></title>
            <link>https://velog.io/@lucas_choi/using-docker-in-ubuntu-container</link>
            <guid>https://velog.io/@lucas_choi/using-docker-in-ubuntu-container</guid>
            <pubDate>Mon, 30 Sep 2024 12:16:25 GMT</pubDate>
            <description><![CDATA[<p>Docker는 일반적으로 호스트 시스템에서 실행되기 때문에, container 내에서 docker를 실행하려면 호스트의 docker 소켓을 컨테이너에 연결해서 사용해야 한다. 이를 통해 호스트 docker 데몬을 직접 컨트롤할 수 있게 된다.</p>
<p>&lt;우분투 컨테이너 실행&gt;
container를 run 할 때, -v /var/run/docker.sock:/var/run/docker.sock 옵션을 추가해줘야 한다. 이는 호스트의 docker 소켓을 컨테이너에 마운트 하여, 컨테이너 내부에서 호스트의 docker 데몬을 사용할 수 있게 해 준다.</p>
<p>하지만... 이미 우분투 컨테이너는 실행되어 있고, commit 하여 container를 백업한 후, 이를 다시 옵션을 추가하여 run 시켜주는 방법이 있는데 번거롭고 귀찮?아서 다른 방법을 사용하기로 했다.</p>
<p>직접 container의 json 파일을 수정해 주는 것!!!!</p>
<pre><code># vim /var/lib/docker/containers/[앞부분이 CONTAINER ID를 포함하는 hash 값(탭 사용하면 편함)]/hostconfig.json</code></pre><p>그러면 수행이... 안 된다....</p>
<p>알아보니 docker는 실행 중인 컨테이너에서 볼륨을 추가하거나 변경하는 기능이 없다고 한다. 그래서 hostconfig.json을 수동으로 수정하는 것을 docker 데몬에 의해 무시되거나 덮어 써질 수 있다.</p>
<p>결국 선택한 방법은 기존 컨테이너의 파일 시스템을 유지한 채로 docker 소켓을 마운트 해서 새로운 컨테이너를 실행하는 것이다.</p>
<ol>
<li><p>마운트 하려는 컨테이너를 종료한다.</p>
<pre><code># docker stop [컨테이너명]</code></pre></li>
<li><p>현재 컨테이너의 상태을 확인해 본다.</p>
<pre><code># docker ps -a
// 여기서 소켓을 마운트 하려는 CONTAINER_ID를 확인한다.</code></pre></li>
<li><p>해당 컨테이너를 이미지로 저장한다.</p>
<pre><code># docker commit [CONTAINER_ID] [저장하고자 하는 이미지 명]</code></pre></li>
</ol>
<p>3-1. 기존 컨테이너를 없애고 수행하자니 불안한데, 기존의 컨테이너명을 동일하게 사용하고 싶어서 컨테이너명을 변경해 주었다.</p>
<pre><code># docker rename [컨테이너명] [바꾸고자 하는 컨테이너명]</code></pre><ol start="4">
<li>docker container를 실행한다.(기존의 설정에 추가)<pre><code># docker run -dit -p xxx22:22 -p xxx80:80 -p xx443:443 -v [도커 볼륨]:[컨테이너 내의 마운트할 경로] -v /var/run/docker.sock:/var/run/docker.sock --name [컨테이너명] [저장했던 이미지명]
</code></pre></li>
</ol>
<p>// 다음은 기존 설정이다.</p>
<h1 id="docker-run--dit--p-xxx2222--p-xxx8080--p-xx443443--v-도커-볼륨컨테이너-내의-마운트할-경로---name-컨테이너명-ubuntu2204">docker run -dit -p xxx22:22 -p xxx80:80 -p xx443:443 -v [도커 볼륨]:[컨테이너 내의 마운트할 경로] --name [컨테이너명] ubuntu:22.04</h1>
<pre><code>
5. 컨테이너에 접속해서 docker를 설치한다.</code></pre><h1 id="apt-get-install--y-dockerio">apt-get install -y docker.io</h1>
<pre><code>
6. 마지막으로 도커가 정상적으로 설치가 되었는지 확인한다.</code></pre><h1 id="docker---version">docker --version</h1>
<p>```</p>
<p>도커 버전을 확인할 수 있으면 이제 docker를 사용할 수 있다!!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[docker volume 설정]]></title>
            <link>https://velog.io/@lucas_choi/docker-volume-%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@lucas_choi/docker-volume-%EC%84%A4%EC%A0%95</guid>
            <pubDate>Sun, 29 Sep 2024 11:17:59 GMT</pubDate>
            <description><![CDATA[<p>docker container로 ubuntu를 실행하고 있는데, container가 내려갈 때마다 ubuntu가 초기화된다는 것을 알았다.</p>
<p>docker container는 가상머신과 다르게 애플리케이션을 가볍고 빠르게 실행하고 제거하는 게 목적이기 때문에, 상태(파일, 데이터 등)는 기본적으로 휘발성이다. 즉, container를 중지하거나 제거하면, container내의 변경된 데이터는 기본적으로 사라진다. 이는 docker의 설계 원칙에 따른 것이다.</p>
<p>이 때문에 데이터 영속성을 보장하려면, 데이터를 저장할 때, docker volume이나 호스트 디렉터리를 컨테이너에 마운트 해야 한다.</p>
<p>이 두 가지 방법 중, 나는 docker volume을 사용하는 방식을 사용하려고 한다. 이 이유는 호스트에서 직접 접근이 불가하기 때문에 실시간 파일 동기화는 안되지만, 호스트 파일 시스템과 분리되어 보안성이 높기 때문이다.</p>
<pre><code>// my_ubuntu_volume라는 이름의 볼륨 생성
# docker volume create my_ubuntu_volume

# docker run -it --name [컨테이너명] -v my_ubuntu_volume:/data ubuntu
// -v 옵션을 사용하여 생성한 볼륨을 ubuntu 컨테이너에 마운트 할 수 있다.
// -it : 터미널 모드로 container 실행
// --name : container 이름을 지정
// my_ubuntu_volume:/data : my_ubuntu_volume을 컨테이너 내의 /data를 volume에 마운트</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[docker nginx 설정하기]]></title>
            <link>https://velog.io/@lucas_choi/docker-nginx-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@lucas_choi/docker-nginx-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 27 Sep 2024 07:55:22 GMT</pubDate>
            <description><![CDATA[<p>Ubuntu container를 설정하는데 갑자기 nginx에서 설정하라고 하네...</p>
<p>nginx가 뭔가 하고 찾아보니...</p>
<p>nginx는 웹 서버 소프트웨어로, 주로 HTTP 서버, 리버스 프록시, 로드 밸런서, 메일 프록시 서버로 사용된다고 한다. 빠른 속도와 높은 성능, 낮은 메모리 사용량으로 많은 웹사이트와 서비스에서 사용된다고 한다.</p>
<p>nginx의 주요 특징</p>
<ol>
<li>웹 서버(Web Server)<ul>
<li>nginx는 웹 서버로, 클라이언트의 HTTP 요청을 받아 웹 페이지를 제공하고 정적 파일(HTML, CSS, 이미지 등)을 제공할 때 매우 효율적이고 빠른 속도를 제공한다.</li>
</ul>
</li>
<li>리버스 프록시(Reverse Proxy)<ul>
<li>리버스 프록시로 동작할 때, nginx는 클라이언트의 요청을 다른 서버로 전달하고 응답을 다시 클라이언트에게 반환한다. 이런 기능 덕분에 웹 애플리케이션의 보안을 강화하고, 부하를 분산시킬 수 있다.</li>
</ul>
</li>
<li>로드 밸런서(Load Balancer)<ul>
<li>여러 서버에 요청을 분배하여 서버 간 부하를 균등하게 나누고, 시스템의 안정성과 성능을 향상시켜 준다. 또한 로드 밸런싱 알고리즘(라운드 로빈, IP 해시 등)을 사용해 효율적으로 트래픽을 관리해 준다.``````</li>
</ul>
</li>
<li>정적 및 동적 콘텐츠 제공<ul>
<li>정적 콘텐츠(HTML, CSS, JavaScript)뿐만 아니라, PHP, Python 등 동적 콘텐츠도 처리할 수 있다.</li>
</ul>
</li>
<li>TLS/SSL 지원<ul>
<li>HTTPS를 통해 보안 연결을 제공하기 위한 TLS/SSL 인증서 설정을 간단하게 할 수 있다.</li>
</ul>
</li>
<li>캐싱(Caching)<ul>
<li>클라이언트 요청에 대한 응답을 캐싱하여, 서버 부하를 줄이고 응답 속도를 향상시킬 수 있다.</li>
</ul>
</li>
<li>확장성(Scalability)<ul>
<li>높을 트래픽을 처리할 수 있는 능력과, 여러 서버를 묶어서 하나의 서비스처럼 보이게 하는 클러스터링 기능을 통해 확장성이 매우 뛰어나다.</li>
</ul>
</li>
</ol>
<p>이제 대강 nginx의 특징을 알아봤으니... 설정을 하러 가보자!!</p>
<pre><code>// nginx 이미지를 다운 받는다.
# docker pull nginx

// nginx가 정상적으로 다운받아졌는지 확인한다.
# docker images

// nginx container를 실행한다.
// ubuntu container를 실행했을 때와 같이 80포트를 내가 원하는 포트로 바인딩해 준다.
# docker container run --name [컨테이너명] -d -p xxxxx:80 nginx</code></pre><p>이러면 기본적인 nginx container 설정을 끝이다.</p>
<p>이제 위에서 지정한 포트 xxxxx를 방화벽 허용해 준 후, 포트포워딩까지 해주고 페이지에 접속해 보면...</p>
<p><img src="https://velog.velcdn.com/images/lucas_choi/post/a57027f1-b298-467e-988d-24edcd5a2c3b/image.png" alt=""></p>
<p>성공적으로 nginx가 적용되는 것을 확인할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[하나의 서버에서 docker를 이용하여 개별 서버 사용하기]]></title>
            <link>https://velog.io/@lucas_choi/%ED%95%98%EB%82%98%EC%9D%98-%EC%84%9C%EB%B2%84%EC%97%90%EC%84%9C-docker%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%98%EC%97%AC-%EA%B0%9C%EB%B3%84-%EC%84%9C%EB%B2%84-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@lucas_choi/%ED%95%98%EB%82%98%EC%9D%98-%EC%84%9C%EB%B2%84%EC%97%90%EC%84%9C-docker%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%98%EC%97%AC-%EA%B0%9C%EB%B3%84-%EC%84%9C%EB%B2%84-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 26 Sep 2024 14:49:14 GMT</pubDate>
            <description><![CDATA[<p>하나의 홈 서버를 구축해서 사용하려고 하는데 친구도 같이 쓰고 싶다고 해서 포트를 열어주고 개인 디렉터리를 설정해 작업할 수 있도록 한 상태이다. 작업하면서 설정관련해 겹치거나 권한 부족으로 인해 요청하는 게 귀찮?고 불편했음.... 그래서 하나의 서버에서 개별 서버를 운영하는 것처럼 하기 위해 자료를 찾아보다가 docker의 ubuntu 컨테이너를 사용해 개별 서버처럼 사용할 수 있다는 글을 보고 환호를 지르며, 이에 대한 포스팅을 하려고 한다.</p>
<p>출처: <a href="https://xo.dev/articles/setup-virtual-environment-for-guests-with-docker">https://xo.dev/articles/setup-virtual-environment-for-guests-with-docker</a>
감사합니다~ ㅎ</p>
<p>이제부터 시작!!!</p>
<p>우선 ubuntu 이미지를 pull 한다.</p>
<pre><code>// 지금 사용하는 버전과 같은 버전을 사용하기 위해 ubuntu 22.04 버전을 사용했다.
# docker pull ubuntu:22.04

// ssh와 web으로 접근하기 위해 22, 80, 443번 포트를 열어줘야 하는데 host의 포트를 그대로 사용할 수 없기 때문에 내가 원하는 임의의 포트로 바인딩해주었다.
# docker run -dit -p xxx22:22 -p xxx80:80 -p xx443:443 --name [컨테이너명] ubuntu:22.04

// 이제 컨테이너에 접속해서 추가적인 설정을 해야 하기 때문에 컨테이너로 접속해 준다.
# docker exec -it [컨테이너명] bash</code></pre><p>이때부터는 현재 사용하고 있는 ubuntu 서버를 시작했을 때, 했던 걸 다시 해준다고 생각하면 된다.</p>
<pre><code>// 패키지 매니저를 업데이트해 주고 vim과 openssh-server 패키지를 설치해 준다.
# apt-get update
# apt-get install vim openssh-server

// ubuntu container로 접속할 새로운 사용자를 생성해 준다.
# useradd [계정ID]

// 사용자 계정의 비밀번호를 변경하고, 이를 통해 ubuntu container에 접속할 예정이다.
# passwd [계정 ID]
// 이후 하라는 대로 비밀번호를 설정하면 된다.

// ssh로 접속할 수 있도록 sshd_config의 설정값을 변경해 준다.
# vim /etc/ssh/sshd_config

// sshd_config의 가장 아래에 아래와 같이 설정을 추가해 주면 된다. 
// Match User [계정 ID]
//         AllowTcpForwarding no
//        X11Forwarding no

// ssh 서버를 실행해서 ssh로 접근할 수 있도록 해준다. 그럼 이제 사용자는 ssh root@[ip주소] -p xxx22로 ubuntu container에 접속할 수 있다.
# service ssh start
</code></pre><p>위 작업에서 기존에 사용했던 systemctl을 사용해 openssh-server를 시작하려고 했으나...
container에서는 systemctl을 사용할 수 없다고 한다. 그래서... service를 사용하시면 됩니다!!!!</p>
<p>이제 끝...인 줄 알았으나...</p>
<p>진짜 마지막으로 해당 container로 접속할 수 있도록 포트포워딩을 해주어야 한다.</p>
<p>위에서 ssh접속 기본 포트인 22번 포트를 xxx22에 바인딩했으므로 </p>
<pre><code># sudo ufw allow xxx22/tcp</code></pre><p>해당 포트를 허용해 준다.(공유기에서도 해당 포트를 포트포워딩을 설정해주어야 한다.)</p>
<p>아래의 명령어로 terminal에서 ssh 접속을 시도하면 이제 진짜진짜로 접속이 되는 것을 확인할 수 있다.</p>
<pre><code># ssh -p [공유기에서 포트포워딩 해준 포트] [계정 ID]@[ip 또는 도메인]</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[ssh 각각의 user에 따라 포트 분배 설정]]></title>
            <link>https://velog.io/@lucas_choi/ssh-%EA%B0%81%EA%B0%81%EC%9D%98-user%EC%97%90-%EB%94%B0%EB%9D%BC-%ED%8F%AC%ED%8A%B8-%EB%B6%84%EB%B0%B0-%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@lucas_choi/ssh-%EA%B0%81%EA%B0%81%EC%9D%98-user%EC%97%90-%EB%94%B0%EB%9D%BC-%ED%8F%AC%ED%8A%B8-%EB%B6%84%EB%B0%B0-%EC%84%A4%EC%A0%95</guid>
            <pubDate>Thu, 26 Sep 2024 13:08:50 GMT</pubDate>
            <description><![CDATA[<p>하나의 포트로 접속하는 경우 발생할 수 있는 문제</p>
<ul>
<li>접속 충돌 : 특정 서버에 여러 사용자가 동시 접속을 시도할 경우, 해당 포트로의 접속 요청이 많아져 과부하가 발생할 수 있다. 특히 관리 목적으로 특정 사용자만 접속해야 하는 경우에도 문제가 될 수 있다.</li>
<li>로그 추적의 복잡성 : 모든 사용자가 같은 포트로 접속하기 때문에 접속 기록이 섞이게 되고, 특정 사용자의 접속 활동을 추적하거나 분석하는데 불편함이 발생할 수 있다.</li>
</ul>
<p>이에 대한 해결방안으로는 각각의 사용자(계정)가 다른 포트를 사용해 접속하도록 설정하면 보안성 향상과 접속 관리의 용이성을 모두 확보할 수 있다.</p>
<p>ssh 설정 파일에서 사용자별로 다른 접속포트를 지정해 줄 수 있다.</p>
<pre><code># sudo vim /etc/ssh/sshd_config

// 파일의 사용자별 포트를 지정

#Port 22 -&gt; 기본 포트는 22로 되어있는 경우 보안에 취약할 수 있어 변경했다.
Port 11111 # root 접속 포트]
Port 22222 # 게스트(user1) 계정 접속 포트

...

Match User user1
    AllowTcpForwarding no
    X11Forwarding no</code></pre><p>이렇게 설정한 후 </p>
<pre><code># systemctl restart sshd</code></pre><p>로 openssh-server를 재시작해주고
필요에 따라 공유기 설정에서 해당 포트를 포트포워딩을 해주면 완료된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[객체지향프로그래밍(OOP: Object Oriented Programming)]]></title>
            <link>https://velog.io/@lucas_choi/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-OOP</link>
            <guid>https://velog.io/@lucas_choi/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-OOP</guid>
            <pubDate>Mon, 16 Sep 2024 17:32:44 GMT</pubDate>
            <description><![CDATA[<p>객체 지향 프로그래밍은 현실 세계의 모든 사물들을 소프트웨어의 코드로 표현하기 위해 각 사물들을 객체(Object)로 보고, 해당 객체의 핵심적인 개념 및 기능만을 추출하는 추상화(Abstraction)를 통해 소프트웨어를 모델링하는 패러다임을 말한다.</p>
<p>객체 지향적 설계를 통해 소프트웨어를 설계 및 개발하면, 코드의 재사용을 통해 반복적인 코드를 최소화하고, 보다 유연하고 변경에 용이한 프로그램을 작성할 수 있다.</p>
<h2 id="객체-지향의-특징-캡상추다">객체 지향의 특징 (캡.상.추.다.)</h2>
<h3 id="1-캡슐화">1. 캡슐화</h3>
<p>서로 연관이 있는 속성과 기능들을 하나의 캡슐로 만들어 외부로부터 데이터를 보호하는 것</p>
<ol>
<li>데이터 보호 - 외부로부터 클래스에 정의된 속성과 기능들을 보호</li>
<li>데이터 은닉 - 내부의 동작은 감추고 외부에는 필요한 부분만 노출</li>
</ol>
<h3 id="2-상속">2. 상속</h3>
<p>클래스 간 공유될 수 있는 속성과 기능들을 상위 클래스로 추상화하여, 해당 상위 클래스로부터 상속받은 여러 개의 하위 클래스들이 모두 상위 클래스의 속성과 기능들을 간편하게 사용하도록 하기 위한 기능</p>
<ul>
<li>상위 클래스의 특성을 하위 클래스에서 상속하고 추가로 필요한 특성을 더하여 확장</li>
<li>상속관계에서는 &quot;하위 클래스는 상위 클래스이다.&quot;를 반드시 만족(is-a 관계)<blockquote>
<ul>
<li>객체 is a 클래스<ul>
<li>김연아 is a 사람 -&gt; 김연아는 한 명의 사람이다.</li>
</ul>
</li>
</ul>
</blockquote>
<ul>
<li>하위 클래스 is a kind of 상위 클래스<ul>
<li>펭귄 is a kind of 동물 -&gt; 펭귄은 동물의 한 분류이다.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="3-추상화">3. 추상화</h3>
<p>특정 사물이나 표상을 해당 표상의 성질, 공통성, 본질에 착안하여 소프트웨어의 모델로 추출</p>
<ol>
<li>데이터 추상화 - 객체들의 공통 특징을 묶어 이름을 붙이는 것
ex) 스마트폰 객체를 추상화를 통해 정보 분리를 하면 스마트폰 =&gt; 휴대폰 =&gt; 통신기기 =&gt; 전자제품으로 추상화할 수 있다.<pre><code># 전자제품
public class Electronics {
 // 전원기능
 power() {}
}
</code></pre></li>
</ol>
<h1 id="통신기기-전자제품을-상속">통신기기 (전자제품을 상속)</h1>
<p>public class CommunicationDevice extends Electronics {
    // 통화기능
    call() {}
}</p>
<h1 id="휴대폰-통신기기를-상속">휴대폰 (통신기기를 상속)</h1>
<p>public class MobilePhone extends CommunicationDevices {
    // 카메라 기능
    camera() {}</p>
<pre><code>// 게임 기능
game() {}</code></pre><p>}</p>
<h1 id="스마트폰-휴대폰을-상속">스마트폰 (휴대폰을 상속)</h1>
<p>public class SmartPhone extends MobilePhone {
    // 추가 기능
    additionalFunction() {}</p>
<pre><code>// 스마트폰은 상위 객체의 기능을 상속을 통해 모두 사용할 수 있다.
// 사용 가능 기능
// power, call, camera, game</code></pre><p>}</p>
<pre><code>
2. 제어 추상화 - 어떤 클래스를 사용하는 사용자에게 해당 메서드의 작동 방식과 같은 내부 로직을 숨기는 것 (사용자 입장에서는 원하는 결과 값만을 필요로 하기 때문에 내부 로직을 알아야 할 필요가 없다.)</code></pre><p>SmartPhone phone = new SmartPhone;</p>
<p>phone.power();
// 전원을 켜거나 끄기 위한 기능을 사용하는데
// Electronic power()의 내부 로직을 알아야 할 필요가 없음</p>
<pre><code>
3. 데이터를 하나로 뭉치기 위한 객체의 경우 - 캡슐화 원칙 일부 위배
    - 캡슐화 원칙에는 일부 위배되긴 하지만 다른 목적을 가진 클래스와 객체를 추상화하는 기법으로 이러한 경우는 행위가 아닌 데이터가 위주이다.
    - 캡슐화의 원칙을 준수하여 모든 필드를 private로 직접 접근을 막고, 각 필드값을 변경하거나 반환하는 메서드를 세트로 미리 작성 (getter, setter)
    - 주로 계층 간 데이터를 주고받을 목적으로 사용
    - 캡슐화 원칙에 따라 작성을 하긴 하지만, 실제로는 캡슐화가 의미가 없을 정도로 필드명을 그대로 사용한 설정자와 접근자로 인해 캡슐화 효과가 없다 - 유지 보수성 약화
    - 하지만 데이터를 주로 다루는 객체의 경우 행위를 추상화하지 않고 미리 모든 필드에 접근 가능성을 염두해 두고 작성해 두는 관례로 인해 현재도 많이 사용되고 있음(엄밀히 말하자면 EJB의 java bean 작성 규칙에 따르는 것)


### 4. 다형성
어떤 객체의 속성이나 기능이 상황에 따라 여러 가지 형태를 가질 수 있는 성질
</code></pre><p>public class Animal {
    public String name; // 이름</p>
<pre><code>public void showInfo() {
    System.out.println(name); // 이름 출력
}</code></pre><p>}</p>
<pre><code>
1. 오버로딩
한 클래스 내에 동일한 메서드 명으로 기술하는 것
- 사용 이유
        1. 메서드에 사용되는 이름을 절약할 수 있다.
        2. 메서드를 호출할 때, 전달해야 할 매개변수의 타입이나 개수에 대해 크게 신경 쓰지 않고 호출할 수 있음.

&gt;```
&gt;public class Penguin extends Animal {
&gt;    public String habitat; // 서식지
&gt;    
&gt;    public Penguin(String name) {
&gt;        this.name = name;
&gt;    }
&gt;    
&gt;    // 오버로딩 - 중복 정의 : 같은 메서드 이름, 다른 인자 리스트
&gt;    public Penguin(String name, String habitat) {
&gt;        this.name = name;
&gt;        this.habitat = habitat;
&gt;    }
&gt;}
&gt;```

- 사용 조건
    1. 클래스 내에서 메서드명 동일
    2. 매개변수의 개수 또는 타입이 다름
    3. 리턴 타입은 오버로딩 구현에 영향을 주지 않음
        ex) 매개변수가 같고 리턴 타입이 다른 경우, 오버로딩 성립이 안됨</code></pre><p>class Overloading {
    // 리턴 타입이 달라도 오버로딩은 성립
    int print(int a, int b) {
        return a + b;
    }</p>
<pre><code>// 매개변수는 같지만 리턴 타입이 다른 경우
// 오버로딩이 성립하지 않음
void print(int a, int b) {
    System.out.println(a + b);
}

// 리턴 타입이 다른 경우
// 위 경우는 매개변수의 타입이 다르므로 오버로딩 성립
double print(double a, double b) {
    return a + b;
}</code></pre><p>}</p>
<pre><code>
2. 오버라이딩
상속받은 부모 클래스에서 이미 정의된 메서드를 자식 클래스에서 재정의하는 것 (단, private 제외)
- 자식 클래스에서 메서드를 재정의하면 부모 클래스의 메서드가 아닌 자식 클래스의 메서드가 실행 (즉, 재정의된 메서드가 우선권을 가짐)
- @Override라는 어노테이션을 사용(어노테이션은 오버라이딩을 검증하는 기능)</code></pre><p>public class Penguin extends Animal {
    public String habitat; // 서식지</p>
<pre><code>// 오버라이딩 - 재정의 : 상위 클래스의 메서드와 같은 이름, 같은 인자 리스트
@Override
public void showInfo() {
    System.out.println(name + &quot; &quot; + habitat);
}</code></pre><p>}</p>
<pre><code>- 사용 조건
  1. 부모 클래스의 메서드와 이름이 동일
  2. 매개변수의 타입, 개수, 순서가 일치
  3. 리턴 타입이 동일
  4. 접근 제한자는 부모 클래스의 메서드와 같거나 더 넓은 범위
  5. 부모 메서드의 예외와 같거나 예외의 개수를 줄일 수 있음.</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Fedora Linux란?]]></title>
            <link>https://velog.io/@lucas_choi/Fedora-Linux%EB%9E%80</link>
            <guid>https://velog.io/@lucas_choi/Fedora-Linux%EB%9E%80</guid>
            <pubDate>Mon, 16 Sep 2024 12:51:04 GMT</pubDate>
            <description><![CDATA[<h2 id="개요">개요</h2>
<h3 id="페도라의-주요-특징">페도라의 주요 특징</h3>
<ol>
<li>최신의 기술이 빠르게 도입된다.
 RPM기반의 새로운 패키지나 기능들이 가장 먼저 패치되는 리눅스 배포판이다. 개발자 혹은 IT 엔지니어가 최신 소프트웨어나 기술을 테스트하고 사용해 보기에 좋다.</li>
<li>레드햇 기반의 리눅스이다.
 대개 기업에서 많이 사용하는 CentOS와 마찬가지로, 레드햇 시스템에 기반한 리눅스 배포판이다.</li>
<li>SELinux의 사용
 Security-Enhanced Linux 시스템이 기본적으로 적용되어 리눅스 시스템의 보안을 강화하는데 도움을 준다.</li>
<li>커뮤니티 중심의 개발
 페도라는 레드햇의 후원 하에 커뮤니티 주도로 개발되기 때문에 커뮤니티 피드백과 기여가 활발한 편이다.</li>
</ol>
<h3 id="우분투와의-차이점">우분투와의 차이점</h3>
<ol>
<li>패키지 관리의 차이
 우분투에서는 apt/apt-get/apt-cache 명령어를 사용했다면, 페도라와 같은 레드햇 기반 시스템에서는 dnf/yum/rpm을 사용한다. 각각 패키지 설치 및 관리를 위한 명령어 사용법이 다르며, 사용하는 패키지의 형식도 다르다.</li>
<li>사용자 기반
우분투 리눅스는 일반 사용자와 서버 사용자들 사이에서 많이 사용되는데, 특히 데스크톱 사용자에게 더 친화적인 환경을 제공한다. 반면 페도라는 엔터프라이즈 환경이나, 개발자들 사이에서 많이 사용된다.</li>
</ol>
<h2 id="페도라에서-사용되는-명령어">페도라에서 사용되는 명령어</h2>
<p>패키지 관련 명령어는 dnf(Fedora 혹은 RHEL/CentOS에서 사용 - yum의 개선된 버전)과 yum 중 필요한 것을 사용하면 된다.</p>
<p>추가로 dnf 또는 yum의 사용이 불가할 때, 저수준으로 패키지를 다루기 위한 명령어인 rpm을 사용할 수도 있다.</p>
<ol>
<li>패키지 검색 및 조회<pre><code># 1. 패키지 검색
dnf search &lt;패키지명&gt;
yum search &lt;패키지명&gt;
</code></pre></li>
</ol>
<h1 id="2-패키지-정보-조회">2. 패키지 정보 조회</h1>
<p>dnf info &lt;패키지명&gt;
yum info &lt;패키지명&gt;</p>
<h1 id="이미-설치된-패키지의-정보를-조회할-경우">이미 설치된 패키지의 정보를 조회할 경우</h1>
<p>sudo rpm -qi &lt;패키지명&gt;</p>
<h1 id="패키지-검증">패키지 검증</h1>
<p>sudo rpm -V &lt;패키지명&gt;</p>
<h1 id="설치된-패키지의-목록-보기">설치된 패키지의 목록 보기</h1>
<p>sudo rpm -qa</p>
<pre><code>
2. 모든 패키지 업데이트
redhat 계열에서는 upgrade 계통의 명령어를 사용하지 않고, update 수행 시 업그레이드 프로세스를 함께 수행한다.</code></pre><p>sudo dnf update
sudo yum update</p>
<pre><code>
3. 캐시 삭제</code></pre><p>sudo dnf clean all
sudo yum clean all</p>
<pre><code>
4. 패키지 삭제</code></pre><p>sudo dnf remove &lt;패키지명&gt;
sudo yum remove &lt;패키지명&gt;
sudo rpm -e &lt;패키지명&gt;</p>
<pre><code>
5. 소프트웨어 설치</code></pre><h1 id="직접-패키지명을-명시하거나-rpm-파일에-대한-상대경로를-명시해도-된다">직접 패키지명을 명시하거나, *.rpm 파일에 대한 &#39;상대경로&#39;를 명시해도 된다.</h1>
<p>sudo dnf install &lt;패키지명&gt;
sudo yum install &lt;패키지명&gt;</p>
<p>sudo rpm -ivh &lt;패키지파일명&gt;.rpm</p>
<h1 id="소프트웨어-그룹-설치">소프트웨어 그룹 설치</h1>
<p>sudo dnf groupinstall &quot;&lt;그룹명&gt;&quot;
sudo yum groupinstall &quot;&lt;그룹명&gt;&quot;</p>
<pre><code>
### 방화벽
우분투에서는 기본적으로 방화벽 소프트웨어를 ufw를 사용했지만, 페도라에서는 firewalld라는 패키지를 사용한다.

1. 방화벽 상태 관련</code></pre><h1 id="상태-체크">상태 체크</h1>
<p>sudo systemctl status firewalld</p>
<h1 id="방화벽-실행">방화벽 실행</h1>
<p>sudo systemctl start firewalld</p>
<h1 id="방화벽-중지">방화벽 중지</h1>
<p>sudo systemctl stop firewalld</p>
<h1 id="방화벽-자동-시작-설정">방화벽 자동 시작 설정</h1>
<p>sudo systemctl enable firewalld</p>
<h1 id="방화벽-자동-시작-설정-해제">방화벽 자동 시작 설정 해제</h1>
<p>sudo systemctl disable firewalld</p>
<pre><code>
2. Zone 관련 명령어
우분투와 달리 페도라의 방화벽 시스템은 네트워크 인터페이스를 Zone이라는 개념으로 구분하여, 각각의 Zone서로 다른 방화벽 규칙을 가질 수 있고, 신뢰 수준에 따라 설정을 다르게 할 수 있다.
예를 들어, public, home, trusted와 같은 영역이 제공되며, 각 영역마다 허용되는 트래픽의 수준이 다르다.
</code></pre><h1 id="현재-활성화된-zone-확인">현재 활성화된 Zone 확인</h1>
<p>sudo firewall-cmd --get-active-zones</p>
<h1 id="기본-zone-확인">기본 Zone 확인</h1>
<p>sudo firewall-cmd --get-default-zone</p>
<h1 id="기본-zone-설정">기본 Zone 설정</h1>
<p>sudo firewall-cmd --set-default-zone=&lt;zone이름&gt;</p>
<h1 id="sudo-firewall-cmd---set-default-zonehome">sudo firewall-cmd --set-default-zone=home</h1>
<h1 id="특정-네트워크-인터페이스를-zone에-추가">특정 네트워크 인터페이스를 Zone에 추가</h1>
<p>sudo firewall-cmd --zone=&lt;zone이름&gt; --change-interface=&lt;인터페이스명&gt;</p>
<h1 id="sudo-firewall-cmd---zonepublic---change-interfaceeth0">sudo firewall-cmd --zone=public --change-interface=eth0</h1>
<pre><code>
3. 서비스 추가 및 제거
- 런타임 레벨: 현재 로그인된 세션에만 적용되며, 재부팅 시 해제됨
- 영구 레벨: 영구적으로 적용(명령어 마지막에 &#39;--permanent&#39;를 추가해 주면 됨)
</code></pre><h1 id="현재-zone에-서비스-추가런타임-레벨">현재 Zone에 서비스 추가(런타임 레벨)</h1>
<p>sudo firewall-cmd --zone=<zone> --add-service=<service></p>
<h1 id="sudo-firewall-cmd---zonepublic---add-servicehttp">sudo firewall-cmd --zone=public --add-service=http</h1>
<h1 id="현재-zone에-서비스-추가영구-레벨">현재 Zone에 서비스 추가(영구 레벨)</h1>
<p>sudo firewall-cmd --zone=<zone> --add-service=<service> --permanent</p>
<h1 id="sudo-firewall-cmd---zonepublic---add-servicessh---permanent">sudo firewall-cmd --zone=public --add-service=ssh --permanent</h1>
<h1 id="서비스-삭제영구-레벨">서비스 삭제(영구 레벨)</h1>
<p>sudo firewall-cmd --zone=<zone> --remove-service=<service> --permanent</p>
<h1 id="sudo-firewall-cmd---zonepublic---remove-servicehttp---permanent">sudo firewall-cmd --zone=public --remove-service=http --permanent</h1>
<pre><code>
4. 포트 추가 및 제거</code></pre><h1 id="영구-레벨에서만-설명">영구 레벨에서만 설명</h1>
<h1 id="포트-열기">포트 열기</h1>
<p>sudo firewall-cmd --zone=<zone> --add-port=&lt;포트번호&gt;/&lt;프로토콜&gt; --permanent</p>
<h1 id="sudo-firewall-cmd---zonepublic---add-port8080tcp---permanent">sudo firewall-cmd --zone=public --add-port=8080/tcp --permanent</h1>
<h1 id="포트-닫기">포트 닫기</h1>
<p>sudo firewall-cmd --zone=<zone> --remove-port=&lt;포트번호&gt;/&lt;프로토콜&gt; --permanent</p>
<h1 id="sudo-firewall-cmd---zonepublic---remove-port8080tcp---permanent">sudo firewall-cmd --zone=public --remove-port=8080/tcp --permanent</h1>
<pre><code>
5. 변경된 방화벽 규칙 적용 / 방화벽 설정 다시 로드</code></pre><p>sudo firewall-cmd --reload</p>
<pre><code>
6. 현재 방화벽 설정 확인</code></pre><p>sudo firewall-cmd --list-all</p>
<pre><code>
7. 특정 ip 차단 및 허용</code></pre><h1 id="특정-ip-차단">특정 ip 차단</h1>
<p>sudo firewall-cmd --zone=<zone> --add-rich-rule=&#39;rule family=&quot;ipv4&quot; source address=&quot;&lt;ip주소&gt;&quot; reject&#39; --permanent</p>
<h1 id="sudo-firewall-cmd---zonepublic---add-rich-rulerule-familyipv4-source-address192168010-reject---permanent">sudo firewall-cmd --zone=public --add-rich-rule=&#39;rule family=&quot;ipv4&quot; source address=&quot;192.168.0.10&quot; reject&#39; --permanent</h1>
<h1 id="특정-ip-허용화이트리스트-추가">특정 ip 허용(화이트리스트 추가)</h1>
<p>sudo firewall-cmd --zone=<zone> --add-rich-rule=&#39;rule family=&quot;ipv4&quot; source address&quot;&lt;ip주소&gt;&quot; accept&#39; --permanent</p>
<pre><code>
8. ICMP 차단/허용
ping 명령어 등을 포함한 네트워크 테스트를 위해 많이 사용되는 ICMP 프로토콜에 관한 설정을 제어할 수 있음.</code></pre><h1 id="icmp-차단">ICMP 차단</h1>
<p>sudo firewall-cmd --zone=<zone> --add-icmp-block=echo-request</p>
<h1 id="icmp-허용">ICMP 허용</h1>
<p>sudo firewall-cmd --zone=<zone> --remove-icmp-block=echo-request
```</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[객체지향의 SOLID 설계 원칙]]></title>
            <link>https://velog.io/@lucas_choi/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5%EC%9D%98-SOLID-%EC%84%A4%EA%B3%84-%EC%9B%90%EC%B9%99</link>
            <guid>https://velog.io/@lucas_choi/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5%EC%9D%98-SOLID-%EC%84%A4%EA%B3%84-%EC%9B%90%EC%B9%99</guid>
            <pubDate>Sat, 14 Sep 2024 09:52:13 GMT</pubDate>
            <description><![CDATA[<p>SOLID 설계 원칙은 객체 지향 설계의 다섯 가지 기본 원칙을 의미하며, 이를 잘 준수하여 소프트웨어를 설계하면 유지 보수와 확장성이 좋은 코드를 작성할 수 있다.</p>
<p>SOLID 5 원칙</p>
<ul>
<li>SRP(Single Responsibility Principle) : 단일 책임 원칙</li>
<li>OCP(Open CLosed Principle) : 개방 폐쇄 원칙</li>
<li>LSP(Listov Sunstitution Principle) : 리스코프 치환 원칙</li>
<li>ISP(Interface Segregation Principle) : 인터페이스 분리 원칙</li>
<li>DIP(Dependency Inversion Principle) : 의존 역전 원칙</li>
</ul>
<h3 id="s--srpsingle-responsibility-principle--단일-책임-원칙">S : SRP(Single Responsibility Principle) : 단일 책임 원칙</h3>
<p>클래스(객체)는 단 하나의 책임만 가져야 한다는 원칙
즉, 하나의 클래스는 하나의 기능을 담당하여 하나의 책임을 수행하는 데 집중하여 코드의 유지 보수성을 높이기 위한 설계 방법이다.
만일, 하나의 클래스에 여러 기능이 존재하면, 기능 변경을 수행할 때 수정해야 할 코드가 늘어난다.</p>
<ul>
<li><p>바람직한 예</p>
<pre><code>// 각 클래스는 하나의 책임만을 가진다.
public class User {
  public void updateUserDetails() { /* 사용자 정보 업데이트 로직 */}
}
public class UserDB {
  public void saveUser(User user) { /* DB에 사용자 정보 저장 로직 */ }
}</code></pre></li>
<li><p>바람직하지 않은 예</p>
<pre><code>// User 클래스가 너무 많은 책임을 가지고 있다.
public class User {
  public void updateUserDetails() { /* 사용자 정보 업데이트 로직 */}
  public void saveUser(User user) { /* DB에 사용자 정보 저장 로직도 여기에 */ }
}</code></pre></li>
</ul>
<h3 id="o--ocpopen-closed-principle--개방-폐쇄-원칙">O : OCP(Open CLosed Principle) : 개방 폐쇄 원칙</h3>
<p>클래스는 확장에 열려 있어야 하며, 수정에는 닫혀 있어야 한다.</p>
<p>확장에 열려 있어야 한다 : 새로운 변경 사항 발생 시, 유연하게 코드를 추가함으로써 큰 힘을 들이지 않고 애플리케이션의 기능을 확장할 수 있다.</p>
<ul>
<li>모듈의 확장성을 보장</li>
</ul>
<p>수정에는 닫혀 있어야 한다 : 새로운 변경 사항이 발생했을 때는 객체를 직접적으로 수정을 제한한다.</p>
<ul>
<li><p>객체를 직접적으로 수정하는 것을 제한</p>
</li>
<li><p>새로운 변경 사항이 발생했을 때, 직접적으로 수정해야 한다면 새로운 변경 사항에 유연하게 대응할 수 없는 애플리케이션이라고 한다. -&gt; 유지 보수의 비용 증가로 이어지는 부적절한 상황 -&gt; 따라서 객체를 직접 수정하지 않고도 변경사항을 적용할 수 있게 설계해야 한다.</p>
</li>
<li><p>바람직한 예</p>
<pre><code>public abstract class Shape {
  public abstract void draw(); // 동작
}
</code></pre></li>
</ul>
<p>public class Circle extends Shape {
    @Override
    public void draw() { /* 원 그리기 로직 */ }
}</p>
<p>public class Square extends Shape {
    @Override
    public void draw() { /* 사각형 그리기 로직 */ }
}</p>
<p>public class Line extends Shape {
    @Override
    public void draw() { /* 선 그리기 로직 */ }
}</p>
<pre><code>
- 바람직하지 않은 예</code></pre><p>public class Shape {
    public void Draw(String shapeType) {
        if (shapeType == &quot;Circle&quot;){ /* 원 그리기 로직 <em>/ }
           else if (shapeType == &quot;Square&quot;) { /</em> 사각형 그리기 로직 */ }
        // 새로운 도형을 추가하려면 이 메소드를 수정해야 한다.
    }
}</p>
<pre><code>
### L : LSP(Listov Sunstitution Principle) : 리스코프 치환 원칙
서브(child) 타입은 언제나 기반(parent) 타입으로 교체할 수 있어야 한다.</code></pre><p>parentType a = new child1Type();</p>
<p>a = new child2Type(); // 객체만 갈아끼우기
/// 변수의 본질은 변하지 않는다.</p>
<pre><code>- 업 캐스팅된 상태에서 부모의 메서드를 사용해도 동작이 의도대로 흘러가야 하는 것을 의미
- 기본적으로 LSP 원칙은 부모의 메서드의 오버라이딩을 조심스럽게 따져가며 해야 한다. 부모 클래스와 동일한 수준의 선행 조건을 기대하고 사용하는 프로그램 코드에서 예상치 못한 문제를 일으킬 수 있기 때문이다.
- 예시</code></pre><p>public void myData() {
    // Collection interface 타입으로 변수 선언
    Collection data = new LinkedList();</p>
<pre><code>data = new HashSet(); // 중간ㅇ에 전혀 다른 자료형 클래스를 할당해도 호환됨.

modify(data); // 메소드 실행</code></pre><p>}</p>
<p>public void modify(Collection data) {
    list.add(1); // interface 구조가 잘 잡혀있기 때문에 add 메소드 동작이 각기 자료형에 맞게 보장됨.
    // ...
}</p>
<pre><code>
- 바람직한 예</code></pre><p>public class Bird {
    public void Fly() { /* 비행 로직 */ }
}</p>
<p>public class Sparrow extends Bird {}</p>
<p>public class Ostrich extends Bird {
    @Override
    public void Fly() {
        throw new Exception(&quot;타조는 날 수 없습니다.&quot;);
    }
}</p>
<pre><code>
- 바람직하지 않은 예</code></pre><p>public class Bird {
    public void Fly() { /* 모든 새는 날 수 있다고 가정 */ }
}</p>
<p>public class Sparrow extends Bird {}</p>
<p>public class Ostrich extends Bird { /* 타조 클래스이지만, Fly 메소드는 부적절 */ }</p>
<pre><code>
### I : ISP(Interface Segregation Principle) : 인터페이스 분리 원칙
SRP 원칙이 단일 책임을 강조한다면, ISP는 인터페이스의 단일 책임을 강조하는 것으로 보면 된다. 즉, SRP 원칙의 목표는 클래스 분리를 통해 이루어진다면, ISP 원칙은 인터페이스 분리를 통해 설계하는 원칙이다.
- 다만, ISP 원칙의 주의해야 할 점은 한번 인터페이스를 분리하여 구성한 후, 추후 생기는 문제로 인해 다시 인터페이스를 분리하는 행위는 하지 말아야 한다. -&gt; 인터페이스는 한번 구성하면 될 수 있으면 변해서는 안 되는 정책의 개념으로 사용되어야 한다.


클라이언트는 자신이 사용하지 않는 메서드에 의존하면 안 된다.
- 바람직한 예</code></pre><p>public interface IPrinter {
    public void Print();
}</p>
<p>public interface IScanner {
    public void Scan();
}</p>
<p>public class AllInOnePrinter implements IPrinter, IScanner {
    public void Print() { /*  인쇄 로직 <em>/ }
    public void Scan() { /</em> 스캔 로직 */ }
}</p>
<pre><code>
- 바람직하지 않은 예</code></pre><p>public interface IMachine {
    public void Print();
    public void Scan();
}</p>
<p>public class implements IMachine {
    public void Print() { /* 인쇄 로직 */ }
    public void Scan() { throw new Exception();}
}</p>
<pre><code>
ISP 원칙 위반 예제와 수정하기
스마트폰 종류의 클래스를 구현하기 앞서 인터페이스를 통해 스마트폰을 추상화해보았다.</code></pre><p>public interface SmartPhone {
    public void call(String number); // 통화기능
    public void message(String  number, String text); // 문자기능
    public void wirelessCharge(); // 무선충전기능
    public void AR(); // 증강현실(AR) 기능
    public void Biometrics(); // 생체인식기능
}</p>
<pre><code>만일 최신 스마트폰 기종의 클래스를 구현한다면, 객체의 동작 모두가 필요하므로 ISP 원칙을 만족하게 된다.</code></pre><p>public class NewPhone implements SmartPhone {
    public void call(String number) {}
    public void message(String  number, String text) {}
    public void wirelessCharge() {}
    public void AR() {}
    public void Biometrics() {}
}</p>
<pre><code>
하지만 최신 기종이 아닌 구현 기종의 스마트폰 클래스를 다뤄야 하는 경우 문제가 발생할 수 있다. 구형 스마트폰 클래스를 구형해야 한다면 무선 충전, 생체 인식과 같은 기능은 포함되어 있지 않기 때문이다. 이런 경우에는 추상 메서드 구현 규칙 상 오버라이딩은 하되, 메서드 내부는 빈 공간으로 두거나 예외가 발생하도록 구성해야 한다. 결국 필요하지도 않은 기능을 어쩔 수 없이 구현해야 하는 낭비가 발생하는 것이다.</code></pre><p>public class OldPhone implements SmartPhone {
    public void call(String number) {}
    public void message(String  number, String text) {}
    public void wirelessCharge() {
        console.log(&quot;지원하지 않는 기능입니다.&quot;);
    }
    public void AR() {
        console.log(&quot;지원하지 않는 기능입니다.&quot;);
    }
    public void Biometrics() {
        console.log(&quot;지원하지 않는 기능입니다.&quot;);
    }
}</p>
<pre><code>
따라서 각각의 기능에 맞게 인터페이스를 잘게 분리하여 구성한다. 그리고 잘게 분리된 인터페이스를 추후 구현할 클래스에서 지원하는 기능에 따라 개별적으로 implements하면 ISP 원칙이 적절하게 지켜지게 된다.</code></pre><p>public interface Phone {
    public void call(String number); // 통화기능
    public void message(String number, String text); // 문자기능
}</p>
<p>public interface WirelessChargable {
    public void wirelessCharge();
}</p>
<p>public interface ARable {
    public void AR();
}</p>
<p>public interface Biometricsable {
    public void Biometrics();
}</p>
<pre><code></code></pre><p>public class NewPhone implements Phone, WirelessChargable, ARable, Biometricsable {
    public void call(String number) {}
    public void message(String number, String text) {}
    public void wirelessCharge() {}
    public void AR() {}
    public void biometrics() {}
}</p>
<p>public class OldPhone implements Phone {
    public void call(String number) {}
    public void message(String number, String text) {}
}</p>
<pre><code>









### D : DIP(Dependency Inversion Principle) : 의존 역전 원칙
객체는 저 수준 모듈보다 고수준 모듈에 의존해야 한다는 원칙으로, 가급적 객체의 상속은 인터페이스를 통해 이루어져야 한다는 의미로 해석할 수 있다.

- DIP를 준수하지 않은 코드</code></pre><p>public class Sword {
    private final String NAME;
    private final int DAMAGE;</p>
<pre><code>public Sward(String name, int damage) {
    NAME = name;
    DAMAGE = damage;
}

// 공격 데미지 반환 함수
public int attack() {
    return DAMAGE;
}

// 무기 이름 반환 함수
@Override
public String toString() {
    return NAME;
}</code></pre><p>}</p>
<pre><code>위와 같이 무기인 칼을 구현한 Sward 객체가 있다. 캐릭터는 위와 같은 무기를 장비할 수 있다.
</code></pre><p>public class Character {
    private final String NAME;
    private int health;
    private Sword weapon;</p>
<pre><code>public Character(String name, int health, Sward weapon) {
    NAME = name;
    this.health = health;
    this.weapon = weapon;
}

// 공격 데미지 반환 함수
public int attack() {
    return weapon.attack();
}

// 피격 함수
public void damaged(int amount) {
    health -= amount;
}

// 무기 변경 함수
public void changeWeapon(Sword weapon) {
    this.weapon = weapon;
}

// 캐릭터 정보 출력 함수
public void getInfo() {
    System.out.println(&quot;NAME: &quot; + NAME);
    System.out.println(&quot;HEALTH: &quot; + health);
    System.out.println(&quot;WEAPON: &quot; + weapon.toString());
}</code></pre><p>}</p>
<pre><code>위 Character 객체에서는 Sword라는 무기 외 다른 무기를 사용할 수 없는 구조이다. Character의 인스턴스 생성 시 Sword에 의존성을 가지기 때문이다. 또한 공격 메서드인 attack() 메서드 또한 Sword에 의존성을 가진다.

이 상황에서 다른 무기를 사용하기 위해서는 Character의 코드를 수정해야 한다. 즉, 개방-폐쇄 원칙(OCP)을 위배한다.

- DIP를 준수한 코드</code></pre><p>public interface Weapon {
    // 공격 추상 함수
    int attack();</p>
<pre><code>// 객체 문자열 반환 추상 함수
@Override
String toString();</code></pre><p>}</p>
<pre><code>우선 고수준 모듈인 Weapon 인터페이스를 선언한다. 모든 무기 객체는 Weapon 인터페이스를 상속받게 될 것이다.
</code></pre><p>public class Sword implements Weapon {
    private final String NAME;
    private final int DAMAGE;</p>
<pre><code>public Sword(String name, int damage) {
    NAME = name;
    DAMAGE = damage;
}

@Override
public int attack() {
    return DAMAGE;
}

@Override
public String toString() {
    return NAME;
}</code></pre><p>}</p>
<pre><code>Sword 객체는 Weapon을 상속받았다.
</code></pre><p>public class Character {
    private final String NAME;
    private int health;
    private Weapon weapon;</p>
<pre><code>public Character(String name, int health, Weapon weapon) {
    NAME = name;
    this.health = health;
    this.weapon = weapon;
}

public int attack() {
    return weapon.attack();
}

public void damaged(int amount) {
    health -= amount;
}

public void changeWeapon(Weapon weapon) {
    this.weapon = weapon;
}

public void getInfo() {
    System.out.println(&quot;이름: &quot; + NAME);
    System.out.println(&quot;체력: &quot; + health);
    System.out.println(&quot;무기: &quot; + weapon.toString());
}</code></pre><p>}</p>
<p>```
Character 객체는 기존 Sword 객체에서 더 고수준 모듈인 Weapon을 파라미터로 받는다. 이 덕분에 Sword뿐만 아니라 Weapon을 상속받은 모든 무기를 Character가 사용할 수 있게 된다.</p>
]]></description>
        </item>
    </channel>
</rss>