<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>dev-h99www.log</title>
        <link>https://velog.io/</link>
        <description>백엔드 신입 개발자</description>
        <lastBuildDate>Mon, 08 Jul 2024 14:25:11 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>dev-h99www.log</title>
            <url>https://velog.velcdn.com/images/dev-h99www/profile/a0eb98b9-1498-4e07-97d4-443c95048625/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. dev-h99www.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/dev-h99www" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[리액트 파이프라인 정리]]></title>
            <link>https://velog.io/@dev-h99www/%EB%A6%AC%EC%95%A1%ED%8A%B8-%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@dev-h99www/%EB%A6%AC%EC%95%A1%ED%8A%B8-%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Mon, 08 Jul 2024 14:25:11 GMT</pubDate>
            <description><![CDATA[<p>임시저장</p>
<ul>
<li>깃허브 액션에서 배포할 때 node.js 16.15.0 버전으로 빌드해야됨</li>
</ul>
<p>도커파일</p>
<pre><code>#base image로 nginx 사용, 태그 생략 시 자동 latest
FROM nginx

#root 경로에 app 폴더 생성
RUN mkdir /app

#work dir을 소스코드를 카피할 기본 경로로 설정
WORKDIR /app

# work dir 하위에 build 폴더 생성 /app/build
RUN mkdir ./build

#앞에는 host pc 뒤에는 docker 경로
#현재 Dockerfile이 위치한 같은 경로에  &gt; Docker 컨테이너의 work dir 경로에 복사하라는 명령어
#윈도우 dockerfile이 있는 위치의 build파일을 컨테이너 만들어서 app 밑에 build에 복사하라는 명령어

#host pc의 현재 경로의 build 폴더를 work dir의 build 폴더로 복사
ADD ./build ./build

# nginx의 default.conf를 삭제 nginx의 기본 설정파일 삭제
RUN rm -rf /etc/nginx.conf.d/default.conf

# host pc의 nginx.conf 파일을 아래 경로에 복사
COPY ./nginx.conf /etc/nginx

# 80 포트 오픈
EXPOSE 80

# nginx -g deamon off; 명령어를 실행
# container 실행 시 자동으로 실행할 커맨드 (nginx 서버 데몬으로 시작하기 위한 명령)
CMD [&quot;nginx&quot;, &quot;-g&quot;, &quot;daemon off;&quot;]</code></pre><p>GITHUB ACTIONS WORKFLOW</p>
<pre><code>name: React-server CD 

# 트리거를 수행할 브랜치를 지정합니다.
on:
  push:
    branches: [ main ]

# 환경설정
env:
  DOCKER_IMAGE: ghcr.io/${{ github.actor }}/today-react
  VERSION: ${{ github.sha }}
  NAME: go_cicd
  BUILD_PATH: ${{ github.workspace}}/today/client

jobs:  
  # 빌드 Job
  build:
    name: Build
    runs-on: ubuntu-latest
    # defaults:
    #   run:
    #     working-directory: ${{ env.BUILD_PATH }}
    # strategy:
    #   matrix:
    #     node-version: [16.15.0]

    steps:
      # github repository에서 checkout
      - uses: actions/checkout@v3

      # - name: npm build
      #   uses: actions/setup-node@v3
      #   with:
      #     node-version: ${{ matrix.node-version }}
      # - run: npm install
      # - run: npm run build


      # - name: Use Node.js ${{ matrix.node-version }}
      #   uses: actions/setup-node@v1
      #   with:
      #     node-version: ${{ matrix.node-version }}
      # - run: npm ci
      # - run: npm run build
      #   if: ${{ always() }}


      # docker build 수행
      - name: Set up docker buildx
        id: buildx
        uses: docker/setup-buildx-action@v1
      - name: Cache docker layers
        uses: actions/cache@v2
        with:
          path: /tmp/.buildx-cache
          key: ${{ runner.os }}-buildx-${{ env.VERSION }}
          restore-keys: |
            ${{ runner.os }}-buildx-
      # GitHub 컨테이너 레지스트리에 로그인 후 빌드 &amp; 푸시    
      - name: Login to ghcr
        uses: docker/login-action@v1
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GHCR_TOKEN }}
      - name: Build and push
        id: docker_build
        uses: docker/build-push-action@v2
        with:
          builder: ${{ steps.buildx.outputs.name }}
          push: true
          tags: ${{ env.DOCKER_IMAGE }}:latest
  # 배포 Job
  deploy:
    needs: build  # build 후에 실행되도록 정의
    name: Deploy
    runs-on: [ self-hosted, react-server ] # AWS ./configure에서 사용할 label명
    steps:
      - name: Login to ghcr
        uses: docker/login-action@v1
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GHCR_TOKEN }}
      # 3000 -&gt; 80 포트로 수행하도록 지정
      - name: Docker run
        run: |
          docker stop ${{ env.NAME }} &amp;&amp; docker rm ${{ env.NAME }} &amp;&amp; docker rmi ${{ env.DOCKER_IMAGE }}:latest
          docker run -d -p 80:80 --name go_cicd --restart always ${{ env.DOCKER_IMAGE }}:latest</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[리액트 공부내용 정리 - 01]]></title>
            <link>https://velog.io/@dev-h99www/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%9E%90%EB%A3%8C-01</link>
            <guid>https://velog.io/@dev-h99www/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%9E%90%EB%A3%8C-01</guid>
            <pubDate>Sun, 30 Jun 2024 13:22:58 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>ES2015(ES6)에 클래스 문법이 도입되면서 리엑트에도 리액트 컴포넌트를 만드는 방법이 도입되었다.
16버전 이후 함수 기반의 컴포넌트를 생성하는 방법이 추가되어, 현재는 함수 기반의 컴포넌트 생성 방법 주를 이루고 있다.</p>
</blockquote>
<h1 id="jsx">JSX</h1>
<h2 id="jsx란">JSX란?</h2>
<p>createElement를 이용해 엘리먼트를 정의하면 복잡하기도 하고 가독성도 좋지 않다. 그래서 리액트팀은 ReactElement를 정의하는 간편한 방법으로 JSX문법을 제공한다. 자바스크립트를 확장한 문법으로, ReactElement를 xml문법 형식으로 정의할 수 있다.
단, JSX는 공식적인 자바스크립트 문법이 아니고, 바벨(babel)이라는 트랜스파일링 툴이 필요하다.
바벨은 ES6 + ES Next 문법을 ES5문법으로 변환해주는 도구이다.
추가로 JSX를 순수 리액트로 변환해주는 기능이 포함되었다.</p>
<h1 id="리액트-랜더링">리액트 랜더링</h1>
<p><img src="https://velog.velcdn.com/images/dev-h99www/post/8f76badc-967a-47b6-b89a-251201c96cbc/image.jpg" alt=""></p>
<h3 id="constructor">Constructor</h3>
<p>생성자는 최초에 State를 한번 초기화 할 목적으로 사용한다.</p>
<p>Mount 되는 시점에만 호출되고 Update 시점에는 호출되지 않는다.</p>
<h3 id="getderivedstatefromprops">getDerivedStateFromProps</h3>
<p>props값으로 state값을 변경해야 되는 경우가 있는데,</p>
<p>생성자에서 변경하게 되면 그 값을 이용해서 초기화를 하지 못하기 때문에 (update에선 constructor 호출 x ) </p>
<p>update에도 호출하는 getDerivedStateFromProps()에서 해줘야 props가 state를 변경해준다.</p>
<h3 id="render">render</h3>
<p>리액트 엘리먼트를 정의한다.</p>
<p>생성자 + props로 state를 갱신할 수 있게끔 하는 메소드 이후에 동작하기 때문에</p>
<p>render 함수 안에서 props나  state 이용이 가능하다.</p>
<h3 id="componentdidmount">componentDidMount</h3>
<p>랜더링 이후 componentDidMount함수에서 추가적으로 state를 변경하거나 비동기 함수를 호출한다.</p>
<p>랜더링 단계나 랜더링 이전 단계에서 진행하면 state가 return하기전에 값이 변경 &gt; setState() 호출로 재귀호출에 빠지는 형태가 된다.</p>
<p>render이후로 동작할 내용에서  UI를 별도로 DOM을 조작한다거나 이벤트를 등록하거나 </p>
<p>setTimeout이나 API를 호출해서 , 그 발생한 정보로 state를 변경할 목적으로 사용한다.</p>
<h3 id="shouldcomponentupdate">shouldComponentUpdate</h3>
<p>update되는 시점에서 props랑 state를 받아서 뭐가 업데이트 됐는지 확인할 수 있고, </p>
<p>그 변경된 내용으로 랜더링을 다시 할건지 말건지를 결정할 수 있다.</p>
<p>참고 <a href="https://ko.legacy.reactjs.org/">https://ko.legacy.reactjs.org/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Exception Transaction]]></title>
            <link>https://velog.io/@dev-h99www/Spring-legacy-project-Exception-and-Transaction</link>
            <guid>https://velog.io/@dev-h99www/Spring-legacy-project-Exception-and-Transaction</guid>
            <pubDate>Sat, 02 Mar 2024 07:11:34 GMT</pubDate>
            <description><![CDATA[<p>최근 참여한 프로젝트의 개발 단계가 마무리되었고, 현재는 서비스가 운영 중입니다.</p>
<p>처음 서비스를 배포하면서 깨달은 중요한 점은 바로 에러 코드의 필요성이었습니다. 개발 단계에서 코드를 완벽하게 작성하여 에러가 발생하지 않도록 하는 것이 최우선이겠지만, 사람이 하는일에 완벽함을 기대하기는 어렵습니다.</p>
<p>서비스 운영 중 서버에서 발생한 에러로 긴급 대응이 필요했던 상황이 있었습니다. 특히 운영 중인 서버의 환경(주로 데이터베이스 서버)이 개발 서버와 다르기 때문에, 문제를 진단하는 데 예상보다 많은 시간이 소요되었습니다.</p>
<p>이 문제를 해결하기 위해, 최근에는 기능을 개발할 때 어느 부분에서 오류가 발생했는지 쉽게 파악할 수 있도록 에러 코드를 반환하고 있습니다. 
<img src="https://velog.velcdn.com/images/dev-h99www/post/3f7baf53-d0c9-499f-9f49-77553ddc0320/image.png" alt="">
서비스 레이어에서 비즈니스 로직을 수행하다 특정 조건을 만족하지 못할 경우, 예외(Exception)를 발생시키고, catch 블록에서 발생한 오류의 세부 사항을 명시한 후 해당 코드를 반환합니다.</p>
<p>컨트롤러 레이어에서는 이 코드를 분석하여 오류에 맞는 결과를 담은 response 객체를 브라우저로 반환합니다. 뷰(view) 레이어에서는 치명적이지 않은 오류의 경우 ResponseDTO.success로 객체를 반환받아 다음 단계를 진행하며, 치명적인 오류가 발생하면 fail로 객체를 반환받아 오류 메시지를 출력합니다.</p>
<p><img src="https://velog.velcdn.com/images/dev-h99www/post/7c84b92b-3edf-472a-accc-0036bef7bb2f/image.png" alt="">
<img src="https://velog.velcdn.com/images/dev-h99www/post/1dff6306-ce34-4c23-ab55-33a5716c5ec6/image.png" alt=""></p>
<p>그러던 중 예상치 못한 문제가 발생했습니다. 프로젝트의 AOP설정에서 트랜잭션 관리를 위해 rollbackFor=Exception.class를 지정했기 때문에, try-catch 블록을 사용하여 발생한 예외(Exception)를 처리함으로써, 해당 비즈니스 로직의 트랜잭션에 대한 롤백이 실행되지 않는 상황이 발생한 것입니다.
try-catch 블록 내에서 예외를 처리하고 있기 때문에, AOP를 통한 트랜잭션 관리 시스템이 예외가 발생했음을 인식하지 못하고, 따라서 자동으로 롤백을 수행하지 않는 문제였습니다. </p>
<p>예외를 잡아내어 로직을 처리하더라도, 필요한 경우에는 명시적으로 롤백을 수행해야 할 필요가 있었습니다.</p>
<p>롤백을 수행하기 위해 두 가지 방법을 생각했습니다.</p>
<ol>
<li><p>throw new Exception(&quot;원래 반환하려했던 resultCode&quot;);
이런 방식으로 Exception을 고의적으로 발생시키며 에러 메세지를 담아 컨트롤러에 반환하는 방식으로, 핸들러 메소드에서 error.getMessage()를 통해 메세지를 받는 방법입니다.</p>
</li>
<li><p>TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); 메소드를 호출해 직접 롤백을 수행하는 것 입니다.</p>
</li>
</ol>
<p>두 가지 방법중 저는 2번 방법을 사용해 문제를 해결했습니다. 명시적으로 롤백을 수행하여 트랜잭션을 관리하도록 코드를 수정했습니다.</p>
<p>이번 문제를 해결하며 몇가지 사실을 알게되었습니다.</p>
<ol>
<li><p>저는 트랜잭션 관리가 필요한 메소드에 대해서 어노테이션을 작성하여 트랜잭션을 관리했지만, 프로젝트가 끝난 지금에서야 특정 패키지 내에 있는 모든 클래드와, 메소드들에 대해 AOP설정으로 트랜잭션이 설정 되어있었음을 알게되었습니다.(rollbackFor = Exception.class)
그것도 모르고 열심히 Transactional 어노테이션을 작성했습니다.</p>
</li>
<li><p>또한, 트랜잭션의 전파속성을 모르고, 하나의 메소드에 Transactional 어노테이션을 작성하고, 해당 메소드가 호출하는 메소드에도 Transactional 어노테이션을 작성했습니다.
이번에 여러 자료들을 조사하며 알게된 사실은 트랜잭션에는 전파레벨이 있고, 내부 트랜잭션에 참여하는 외부 트랜잭션은 내부 트랜잭션에 포함되어 Transactional을 작성할 필요가 없다는 사실입니다. (트랜잭션의 전파레벨에 대해서는 새로 포스팅을 작성할 예정입니다.)</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[window docker oracle18c xe 설치하기]]></title>
            <link>https://velog.io/@dev-h99www/window-docker-oracle18c-xe-%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@dev-h99www/window-docker-oracle18c-xe-%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 09 Feb 2024 08:24:05 GMT</pubDate>
            <description><![CDATA[<h1 id="oracle18c-xe-docker로-설치">oracle18c xe docker로 설치</h1>
<p>이번 설 연휴동안 미니프로젝트를 진행하려고, 로컬에 오라클을 설치하려고 하는데요..?
docker로 oracle18c xe 설치하는 과정을 기록하려 합니다.</p>
<h2 id="oracle을-docker-컨테이너로-생성하여-사용하는-이유">Oracle을 docker 컨테이너로 생성하여 사용하는 이유</h2>
<p>예전에 국비지원 학원에 다닐 때, 로컬PC에 오라클을 설치해 사용했었는데요..
DB를 사용하지 않을때도 백그라운드에 계속 돌고있어서, 컴퓨터가 굉장히 버벅거리고, 삭제하는 방법도 복잡해서 아예 포맷을 해버렸던 기억이 있더랬죠..
그래서, 필요할때만 리소스를 할애하고, 필요없어지면 쉽게 삭제할 수 있게끔 Docker로 설치하려 합니다.</p>
<h2 id="준비">준비</h2>
<ol>
<li>docker 설치</li>
<li>git 설치</li>
<li>DBMS 접속 툴( sql developer, dbeaver...) 설치</li>
</ol>
<h2 id="과정">과정</h2>
<ol>
<li><p>오라클에서 제공하는 도커 파일을 사용하기 위해 깃 클론을 받아야합니다.
우선 git을 클론받을 폴더를 생성합니다. 
저는 C:\dev\docker\oracle 디렉토리를 생성해 해당 디렉토리에 git clone을 했습니다.
<code>&gt; git clone https://github.com/oracle/docker-images.git</code></p>
</li>
<li><p>clone이 완료되면 다음 경로로 이동합니다. 
/c/dev/docker/oracle/dockerimages/OracleDatabase/SingleInstance/dockerfiles
터미널에(git bash 사용함) <code>&gt; ls -a</code> 명령어를 입력하면 해당 디렉토리에 buildContainerImage.sh 파일이 있는것을 확인할 수 있습니다.</p>
</li>
<li><p><code>./buildContainerImage.sh --help</code> 명령어를 입력하면 <img src="https://velog.velcdn.com/images/dev-h99www/post/2c467da8-843e-4fe6-a161-62ba8e93297d/image.png" alt="">
위와같은 설명이 나오는데요,,
저는 18c xe를 설치하기 위해 ./buildContainerImage.sh -v 18.4.0 -x 명령어를 입력했습니다.</p>
</li>
<li><p>10분정도 기다리고나면 <img src="https://velog.velcdn.com/images/dev-h99www/post/d057c860-1a49-43b1-8265-bdfe3552724b/image.png" alt="">
위와같은 결과 문구가 출력되며, docker images 명령어를 사용해 이미지가 생성된 것을 확인할 수 있습니다.</p>
</li>
<li><p>터미널에 docker run -i -t -d --hostname ora18xe --name ora18xe -p 1521:1521 -v ~/Docker/shared/:/shared oracle/database:18.4.0-xe 명령어를 입력해 이미지를 통해 컨테이너를 생성합니다.</p>
</li>
<li><p>터미널에 docker logs ora18xe를 입력하면 현재 컨테이너의 생성 진행현황을 확인할 수 있는데요..?
예전에 이부분을 몰라 docker run을 하면 바로 컨테이너가 생성되는줄알았더랬죠...
그것도 모르고 괜히 접속이 안되니까, 오류인가 싶어서 지우고 다시 생성하고 며칠을 고생했습니다...</p>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/dev-h99www/post/64d1f77a-c276-4b1b-9087-d93b2d0240e0/image.png" alt="">
위와같이 현재 도커 컨테이너가 설치되는중입니다.</p>
<p>7.컨테이너의 이용준비가 끝나면 다음과 같은 메세지가 나옵니다.
<img src="https://velog.velcdn.com/images/dev-h99www/post/c8a6a1f6-0fcf-4af8-8e5d-cfd385f2c873/image.png" alt=""></p>
<ol start="8">
<li>DBMS tool로 db 접속해보겠습니다.
<img src="https://velog.velcdn.com/images/dev-h99www/post/014d03fe-40fe-4f2d-ba0e-6bd5e356c6fa/image.png" alt="">
위의 출력메세지에서 db 정보를 찾을 수 있는데요, 8356ae~~ 이부분이 system계정의 비밀번호고, 아래 XEPDB1이 service name입니다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/dev-h99www/post/dbce7e04-f64e-4e2d-9b58-01c00f4343ef/image.png" alt="">
위와같이 Database에 XEPDB1을 적고 service name을 적던가, SID로 변경하고 xe로 입력하면 됩니다. 
password는 위에 docker logs로 확인한 83~~ 문자열을 복붙해서 입력하면 접속이 됩니다.</p>
<ul>
<li>git bash를 계속해서 사용하고있었는데, docker 컨테이너에 진입하는 docker exec명령어는 git bash에서 동작하지 않더라구요?  그래서 그부분부터 powershell로 실행하니 정상적으로 진입했습니다.</li>
</ul>
<p>해당 포스스트는 
<a href="https://www.youtube.com/watch?v=uoQr1j1A6Hk">https://www.youtube.com/watch?v=uoQr1j1A6Hk</a>
영상을 참고하여 작성했습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[최대공약수]]></title>
            <link>https://velog.io/@dev-h99www/%EC%B5%9C%EB%8C%80%EA%B3%B5%EC%95%BD%EC%88%98</link>
            <guid>https://velog.io/@dev-h99www/%EC%B5%9C%EB%8C%80%EA%B3%B5%EC%95%BD%EC%88%98</guid>
            <pubDate>Mon, 17 Jul 2023 13:13:55 GMT</pubDate>
            <description><![CDATA[<h1 id="유클리드-호제법">유클리드 호제법</h1>
<p>A와 B의 최대공약수를 G라고 하면
$$ A = a * G 
$$
$$ B = b *G
$$
라고  표현할 수 있고, 이때 a와 b는 서로소이다.</p>
<p>A와 B의 관계는 다음과 같이 표현할 수 있다.
$$ A = B <em>q + r
$$
이 식에 첫 번째 표현을 대입하면
$$ a*G = b * G * q + r
$$
이렇게 표현된다. 그러므로
$$ r = G</em>( a -Q<em>b).
$$
a - Q</em>n 과 b가 서로소가 아님을 가정하고, 두 수의 공약수 p로 표현하면 다음과 같다.
$$ Let\quad a - Qb = mp,,b=np
\a - Qnp = mp\quad then \quad a = (Qn +m)p
$$
a와 b는 서로소이므로 p = 1이고, r과 b는 서로소이다.
그러므로 r과 B의 최대공약수는 G이다.</p>
<p>이걸 코드로 표현하면 다음과 같다.</p>
<pre><code class="language-java">    public static int gcd(int a, int b) {

        if(b == 0) return a;
        return gcd(b, a % b);
    }</code></pre>
<p>처음 최대공약수를 찾은 방법은 최소값만큼 for문을 돌려 둘다 나눠지는 숫자를 최대공약수에 저장 후 반환하는 방법으로 찾았다. </p>
<pre><code class="language-java">    public static int gcd(int a, int b) {

        int gcd = 1;
        for(int i = 1; i &lt;= (a &lt; b? a: b); i++) {
            if(a % i == 0 &amp;&amp; b % i == 0) gcd = i;
        }

        return gcd;
    }</code></pre>
<p>이 경우 O(N)의 시간복잡도를 갖는데, 유클리드 호제법을 사용하면 O(logN)의 시간 복잡도를 갖는다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring IoC, AOP, PSA]]></title>
            <link>https://velog.io/@dev-h99www/Spring-IoC-AOP-PSA</link>
            <guid>https://velog.io/@dev-h99www/Spring-IoC-AOP-PSA</guid>
            <pubDate>Sat, 15 Jul 2023 03:37:42 GMT</pubDate>
            <description><![CDATA[<h1 id="ioc">IoC</h1>
<p>객체의 생명주기(생성, 소멸 등)의 관리를 개발자가 직접 하는것에서 프레임워크가 하도록 위임한다.</p>
<h3 id="di-의존성-주입">DI 의존성 주입</h3>
<p>A클래스와 B클래스가 있을때, B클래스에서 A클래스의 객체를 사용하는 형태로 설계가 되어있을때, 사용자는 코드에서 new 연산자를 통해 인스턴스를 생성해 주고, B클래스의 인스턴스에 클래스의 인스턴스를 넣어주어야한다. 이를 IoC컨테이너가 해소해주고, 코드 사이 결합도가 낮아진다.</p>
<p>의존성 주입에는 세 가지가 있다</p>
<blockquote>
<ul>
<li>필드 주입</li>
</ul>
</blockquote>
<ul>
<li>세터 주입</li>
<li>생성자 주입</li>
</ul>
<p>생성자 주입에는 필드에 final 키워드를 사용할 수 있다. final 키워드를 쓰려면 그 필드가 초기화 될거라는 보장이 있어야 하는데, 필드에 참조하는 빈을 선언하고 생성자에서 초기화 하는 코드가 작성이 가능하기 때문에 생성자주입만 유일하게 final 키워드를 쓸 수 있다.</p>
<p>순환 참조 발생시 필드주입과 세터주입은 메소드 실행 시점에만 에러가 발생하지만, 생성자 주입은 애플리케이션 실행 시점에 확인이 가능하다.</p>
<blockquote>
<p>순환참조 : A클래스가 B클래스의 Bean을 주입받고, B클래스가 A클래스를 주입받는 상황처럼 순환되어 참조하는 경우.</p>
</blockquote>
<h1 id="aop">AOP</h1>
<p>관점 지향 프로그래밍
클래스들 간의 중복되는 코드를 aspect라고 한다.
이러한 aspect를 클래스마다 작성하는것이 아닌, 하나의 클래스에 작성하고 스프링 컨테이너가 필요한 위치에 소스코드를 넣어서 실행해준다.
낮은 결합도와 높은 응집도를 갖게 해준다.</p>
<h1 id="psa">PSA</h1>
<p>특정 기술에대한 종속성을 낮추기 위해 만들어진 기술.
구현부분을 숨기고, 사용자에게 편의성을 제공한다. 추후에 기술이 변경되어 내부적으로 코드의 수정이 일어나도 구현되는 부분만 변경이 되므로, 사용자가 작성한 코드의 변경이 불필요하다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스/가장 가까운 글자]]></title>
            <link>https://velog.io/@dev-h99www/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4%EA%B0%80%EC%9E%A5-%EA%B0%80%EA%B9%8C%EC%9A%B4-%EA%B8%80%EC%9E%90</link>
            <guid>https://velog.io/@dev-h99www/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4%EA%B0%80%EC%9E%A5-%EA%B0%80%EA%B9%8C%EC%9A%B4-%EA%B8%80%EC%9E%90</guid>
            <pubDate>Tue, 11 Jul 2023 14:57:10 GMT</pubDate>
            <description><![CDATA[<h3 id="문제">문제</h3>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/142086">https://school.programmers.co.kr/learn/courses/30/lessons/142086</a></p>
<h3 id="풀이">풀이</h3>
<p><a href="https://github.com/dev-h99www/codingtest/commit/452305d170326d93247c1ff5eea49f0e891a589a">https://github.com/dev-h99www/codingtest/commit/452305d170326d93247c1ff5eea49f0e891a589a</a></p>
<p><img src="https://velog.velcdn.com/images/dev-h99www/post/5caaa35e-9164-4c8d-b832-6d8e7aedcec3/image.png" alt=""></p>
<p>문제 풀이를 위해 다음과 같은 과정의 전략을 짰다.</p>
<ol>
<li>알파벳 하나하나에 대응되는 배열을 생성해 알파벳의 현재 위치를 저장한다. 초기값은 -1로 설정한다. (현재 위치는 0부터 s.length()이기 때문에 -1로 설정)</li>
<li>문자열 s를 배열로 만들어 for문을 돌려 다음 과정을 반복한다.
2-1. 1번에 생성한 알파벳 배열에 값이 초기화되지 않았으면 answer에 -1을 저장하고, 현재 반복문의 index를 해당 알파벳에 갱신한다.
2-2. 알파벳 배열에 값이 초기화 되었다면, 현재 index에서 저장된 index를 빼 알파벳 간 거리를 구한다.</li>
</ol>
<p>코드는 테스트에 통과했지만 좋은 풀이가 있어 기억하기위해 포스팅을 한다.</p>
<p><img src="https://velog.velcdn.com/images/dev-h99www/post/047fd6a6-33f2-4487-baf1-8ac20690b2f8/image.png" alt="">
1번에 생성한 배열을 Map으로 대체해 코드를 짰다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스/과일 장수]]></title>
            <link>https://velog.io/@dev-h99www/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4%EA%B3%BC%EC%9D%BC-%EC%9E%A5%EC%88%98</link>
            <guid>https://velog.io/@dev-h99www/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4%EA%B3%BC%EC%9D%BC-%EC%9E%A5%EC%88%98</guid>
            <pubDate>Sat, 08 Jul 2023 08:37:42 GMT</pubDate>
            <description><![CDATA[<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/135808">https://school.programmers.co.kr/learn/courses/30/lessons/135808</a></p>
<h1 id="자료구조의-이해와-탐색-삽입의-시간복잡도-이해">자료구조의 이해와 탐색, 삽입의 시간복잡도 이해</h1>
<h2 id="서론">서론</h2>
<p>나는 LinkedList도 C언어로 직접 구현해봐서 LinkedList와 ArrayList의 차이점을 잘 알고있다고 생각했다.
LinkedList는 Node가 다음 노드의 주소를 갖고있어서 
삽입 삭제와 같은경우 node의 주소값만 변경하면 돼서 삽입 삭제에 걸리는 시간은 $$O(1)$$의 시간복잡도를 갖지만,
탐색은 순차적으로 탐색하고, 운이 안좋으면 찾으려는 데이터가 맨 마지막에 위치해서 $$O(n)$$의 시간을 갖는다.</p>
<p>반면 ArrayList는 배열을 기반으로 구현돼서 삽입 삭제의 경우 배열을 통째로 새로 복사해서 삽입 또는 삭제를 하고, 그래서 $$O(n)$$ 인데, index가 존재해서 탐색은 $$O(1)$$이라고 알고있었다.</p>
<h2 id="풀이-과정">풀이 과정</h2>
<ol>
<li>score를 list에 담고, sort한다.</li>
<li>그 후 score의 길이를 m으로 나누어 상자의 갯수를 구한다.</li>
<li>상자의 갯수만큼 상자의 최하 품질의 값을 구해 상자의 총 가격( 최하 품질의 값 * 상자당 과일의 갯수)을 구한다.
3-1. 이걸 상자의 갯수만큼 반복하며 총 가격을 누적해 더한다.</li>
</ol>
<p>처음엔 LinkedList를 사용했다.
ArrayList를 사용하려다 LinkedList를 사용한 이유는 조건이 다음과 같이 주어졌는데,</p>
<blockquote>
<p>3 ≤ k ≤ 9
3 ≤ m ≤ 10
7 ≤ score의 길이 ≤ 1,000,000</p>
</blockquote>
<p>add를 최대 1,000,000번 해주어야 한다면 
ArrayList를 이용해서 구현하면 수많은 데이터 복사가 이루어 질거라 예측했기 때문이다.
물론 list.get 부분은 빠르겠지만.</p>
<pre><code class="language-java">public static int solution(int k, int m, int[] score) {
        int answer = 0;

        LinkedList&lt;Integer&gt; list = new LinkedList&lt;&gt;();

        for(int i = 0; i &lt; score.length; i++) {
            list.add(score[i]);
        }

        Collections.sort(list, (o1, o2) -&gt; o2 - o1);

        for(int i = 1; i &lt;= score.length / m; i++) {

            answer += m * list.get(i * m - 1);

        }
        return answer;
    }</code></pre>
<p><img src="https://velog.velcdn.com/images/dev-h99www/post/3a616bed-be56-4aac-bf22-946423d6b151/image.png" alt="">
하지만 예상과는 달리 시간초과로 실패한 테스트 케이스가 발생했다.</p>
<p>원래 사용하려했던 ArrayList를 사용해 코드를 다시 짰는데 테스트에 통과했다.</p>
<pre><code class="language-java">public static int solution(int k, int m, int[] score) {
        int answer = 0;

        ArrayList&lt;Integer&gt; list = new ArrayList&lt;&gt;();

        for(int i = 0; i &lt; score.length; i++) {
            list.add(score[i]);
        }

        list.sort(Comparator.reverseOrder());
        for(int i = 1; i &lt;= score.length / m; i++) {

            answer += m * list.get(i * m - 1);

        }
        return answer;
    }</code></pre>
<p><img src="https://velog.velcdn.com/images/dev-h99www/post/00a7ddb7-a874-437e-b216-7f6a8a4aa8d5/image.png" alt=""></p>
<p>이유를 알고싶어 ArrayList에 대해 공부하던 중 알게된 사실이 있다.
ArrayList는 Capacity가 존재한다.
초기값은 10이며
<img src="https://velog.velcdn.com/images/dev-h99www/post/bf2347ea-e4ea-4c15-a837-a2fc92dc9bfe/image.png" alt=""></p>
<p>ArrayList 객체는 크기를 증가시킬때 기존 capicity의 1.5배의 크기로 증가시킨다.
<img src="https://velog.velcdn.com/images/dev-h99www/post/13a90fc2-e156-4e26-a4a6-8839481bc26e/image.png" alt=""></p>
<p>결국 문제 조건에 1,000,000의 과일 갯수로 1,000,000번 삽입에 의한 데이터 복사가 이루어지지 않을까 싶었던 내 예측은 틀렸고, 계산해보니 1,000,000번의 삽입으로는 30번의 배열 복사가 이루어진다.</p>
<p>단편적인 특징으로 자료구조를 이해해서 사용하다 이렇게 오류를 겪어보니, 자료구조를 정독해야 할 필요성을 느꼈다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스/명예의 전당(1)]]></title>
            <link>https://velog.io/@dev-h99www/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4%EB%AA%85%EC%98%88%EC%9D%98-%EC%A0%84%EB%8B%B91</link>
            <guid>https://velog.io/@dev-h99www/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4%EB%AA%85%EC%98%88%EC%9D%98-%EC%A0%84%EB%8B%B91</guid>
            <pubDate>Sat, 08 Jul 2023 06:43:49 GMT</pubDate>
            <description><![CDATA[<p>명예의 전당(1)
<a href="https://school.programmers.co.kr/learn/courses/30/lessons/138477">https://school.programmers.co.kr/learn/courses/30/lessons/138477</a></p>
<p><img src="https://velog.velcdn.com/images/dev-h99www/post/cb52c941-a05e-4267-b373-a88b321ce995/image.png" alt="">
문제는 다음과 같다.
매일 한명의 가수가 나와서 노래를 부르고, 명예의전당은 k 순위까지 표시를 한다.
k일차까지는 전원 명예의 전당에 등재되어있다가, k일이 지난 후 부터는 매일 한명씩 탈락이 될 것이다.
이때 매일 <strong>명예의 전당에 올라간 점수</strong>중 가장 낮은 점수가 발표 점수다.</p>
<p>이 문제를 해결하기 위해 </p>
<ol>
<li>우선 k 크기의 배열을 만들어서 명예의 전당을 의미하는 뜻을 사용한다.</li>
<li>새로운 점수가 들어오면 랭크와 비교 후 반영하고, 발표점수를 저장한다.
이렇게 해결하려고 했다.</li>
</ol>
<p>그래서 코드의 흐름은</p>
<ol>
<li>for문으로 다음의 내용을 score의 크기만큼 반복(score의 크기는 일수를 의미하니까)</li>
<li>rank에 score을 반영 후 sorting
2-1. k일 이전에는 rank의 i의 index만큼 sorting 후 i를 result에 저장.
2-2. k일 이후에는 rank의 k-1과 새로운 score을 비교 후 더 큰값을  rank에 남기고 작은수는 제외
2-2-1. 이때 새로운 score가 최소값이면 종료. 아니면 다시 sorting</li>
</ol>
<p>이렇게 계획하고 풀려는데... 뭔가 좀 복잡하다.</p>
<p>그래서 다시 고민을 해봤다.</p>
<p>그러다 List의 sort기능을 생각했고,
List로 score을 순차적으로 저장하며 정렬해서,
k일 이전에는 일차의 인덱스를 꺼내고,
k일 이후에는 계속 k번째 인덱스만 꺼내서 result에 추가하면 더욱 간단해 질 것 같았다.</p>
<pre><code class="language-java">public static int[] solution(int k, int[] score) {

        int[] answer = new int[score.length];
        List&lt;Integer&gt; rank = new ArrayList&lt;&gt;();

        for(int i = 0; i &lt; score.length; i++) {
            rank.add(score[i]);
            rank.sort(Comparator.reverseOrder());

            if(i &lt; k) {
                answer[i] = rank.get(i);
            } else {
                answer[i] = rank.get(k - 1);
            }
        }

        return answer;
    }
</code></pre>
<p><img src="https://velog.velcdn.com/images/dev-h99www/post/4b736f89-7f0b-40a3-ac4f-9ceb8fbcbd68/image.png" alt=""></p>
<p>생각했던대로 잘 실행돼서 기뻐하던 차에 다른사람들의 풀이를 보는데 많은 사람들이 PriorityQueue를 사용해서 풀었다.</p>
<p>그래서 PriorityQueue를 공부할 목적으로 이 포스팅을 작성한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스/달리기 경주]]></title>
            <link>https://velog.io/@dev-h99www/programmers-178871</link>
            <guid>https://velog.io/@dev-h99www/programmers-178871</guid>
            <pubDate>Tue, 04 Jul 2023 13:16:53 GMT</pubDate>
            <description><![CDATA[<p>코딩테스트 시리즈에는 문제를 해결하다 막힌것, 중요하다고 생각하는 문제 등 복습이 필요한것들을 포스팅하기 위해 만들었습니다.
나머지 문제들은 <a href="https://github.com/dev-h99www/codingtest">https://github.com/dev-h99www/codingtest</a> 에 올릴 생각입니다.</p>
<h1 id="달리기-경주">달리기 경주</h1>
<p>2023-07-04
<img src="https://velog.velcdn.com/images/dev-h99www/post/18260e6a-6935-4dee-a6e5-28145abced04/image.png" alt=""></p>
<p>String 배열에 경주마들의 이름이 저장되어있고, 인덱스가 현재 순위를 의미한다.
callings는 경주마가 호명된 순서대로 경주마의 이름이 저장되어 있는 String 배열이다.</p>
<p>예를들어 a, b, c의 경주마가 달리고있는데 b가 호명되면 b가 앞의 말을 추월했다는 뜻이고,ㅈㅈ
그러면 현재 순위는 b, a, c 이다.</p>
<p>문제 해결을 위한 내가 생각한 순서도는 다음과 같다. </p>
<p><img src="https://velog.velcdn.com/images/dev-h99www/post/744713fe-9282-4f57-889f-95e746e2e2a5/image.png" alt=""></p>
<p>아이디어를 바탕으로 코드를 짰다.</p>
<pre><code class="language-java">class Solution {
    public String[] solution(String[] players, String[] callings) {

        String[] answer = {};
        String temp = &quot;&quot;;

        for(int i = 0; i &lt; callings.length; i++) {
            int index = 0;

            for(int j = 0; j &lt; players.length; j ++) {
                if(callings[i].equals(players[j])) {            
                    temp = players[j -1];
                    players[j - 1] = players[j];
                    players[j] = temp;

                    break;
                }
            }
        }

        return players;
    }
}</code></pre>
<p>결과는 다음과 같다
<img src="https://velog.velcdn.com/images/dev-h99www/post/3a786546-d489-4c08-a1d4-6862d73ad1c0/image.png" alt=""></p>
<p>시간 복잡도를 생각해보니 최대 O($$n^2$$)이다.</p>
<p>지금 당장 생각나는 해결법은 탐색부분과 스왑부분에서 성능이 좋은 메소드를 제공해주는 자료구조를 사용하는 것인데, 스왑부분에선 더 시간을 유의미하게 줄일 수 있을지 모르겠다.</p>
<p>문제 해결을 위해 배열을 사용해 index를 탐색하는 것 보다 더 빠른 방법이 없을지 다른 자료구조를 사용하는 것에 대해 고민해봐야겠다.</p>
<h3 id="해결">해결</h3>
<p>2023-7-8
<img src="https://velog.velcdn.com/images/dev-h99www/post/7294d23f-811e-4a5b-909b-f5c0fd51a78f/image.png" alt=""></p>
<p>이전에는 callings를 순차적으로 조회하며 매번 callings[i]의 값이 몇위인지 조회하고, 조회한걸 토대로 players를 갱신해주었다.</p>
<p>그래서 callings 반복 * players 순위조회의 시간이 걸렸는데</p>
<p>순위를 따로 저장해두면 callings 반복에서 나온 선수이름이 몇위인지 바로 비교해서
players를 갱신할 수 있다.</p>
<p>해결하게된 계기는, 탐색을 빨리할 수 없을까 고민하다 Map을 사용하는건 어떨까 생각하고 있었다.그러던중 면접때 알고리즘테스트를 봤는데, 그때 피드백받은 부분이 매번 값을 구하지 말고, ArrayList에 차곡차곡 저장해놓으면 매번 n번씩 반복하는걸 안할 수 있다는 것이었다.
Map에 rank를 저장해두고, 참조하면 for문안에서 rank를 구하던 과정을 줄일 수 있다는 생각에 코드를 작성하고, 통과할 수 있었다.</p>
<pre><code class="language-java">
        Map&lt;String, Integer&gt; rankMap = new HashMap&lt;&gt;();

        for(int i = 0; i &lt; players.length; i++) {
            rankMap.put(players[i], i);
        }

        for(String calling : callings) {
            //호명될 선수의 랭킹
            int rank = rankMap.get(calling);

            //랭킹으로 호명된 선수와, 추월당한 선수 저장
            String passed = players[rank - 1];
            String called = players[rank];

            //맵에 랭크 변경
            rankMap.put(passed, rank);
            rankMap.put(called, rank - 1);

            //원래 배열에 순위 반영
            players[rank] = passed;
            players[rank - 1] = called;
        }
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[window환경에서 docker로 mysql 사용하기]]></title>
            <link>https://velog.io/@dev-h99www/window-docker-mysql</link>
            <guid>https://velog.io/@dev-h99www/window-docker-mysql</guid>
            <pubDate>Fri, 30 Jun 2023 15:02:51 GMT</pubDate>
            <description><![CDATA[<p>Docker로 Mysql 설치하면서 사용한 명령어 기록</p>
<pre><code class="language-terminal">-- 이미지 다운받기
docker search mysql // mysql 이미지를 도커허브에서 검색한다.
docker pull mysql // 어떤걸 다운받을까 봤는데, 사용자가 가장 다운 많이받은 mysql을 다운받는다.
docker images // 도커 이미지가 잘 다운받아졌는지 확인한다.

-- 이미지로 컨테이너 생성
docker run --name mysql_local -p 3306:3306 -e MYSQL_ROOT_PASSWORD=&quot;mypassword&quot; -d mysql
// --name은 컨테이너의 이름을 설정한다.
// -p는 포트번호 설정, 컨테이너의 포트번호와 내 pc의 포트번호 연결, 
// -d는 컨테이너를 생성할 이미지의 이름

-- 컨테이너 접속
docker ps -a // 컨테이너 상태를 확인한다. 잘 생성되어 실행중인지 확인한다.
docker exec --user=&quot;root&quot; -it mysql_local bash // 생성된 컨테이너를 실행한다.

//컨테이너 내부
mysql --user=root --password=&quot;mypassword&quot; // mysql콘솔이 정상적으로 나오면 성공. 
// 확인 후 다시 터미널로 돌아오려면 exit나 quit 명령어를 사용하자.</code></pre>
<p>이후 DBMS 클라이언트로 db에 접속</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[MySQL NULL 처리]]></title>
            <link>https://velog.io/@dev-h99www/mysql-null-handling</link>
            <guid>https://velog.io/@dev-h99www/mysql-null-handling</guid>
            <pubDate>Fri, 30 Jun 2023 13:54:12 GMT</pubDate>
            <description><![CDATA[<p>oracle에서 NVL로 null처리를 했는데, MySQL에서 NVL함수가 없어서 NULL을 처리하는 몇 가지 방법을 정리합니다.</p>
<ol>
<li><p>IF NULL</p>
<p> <code>SELECT IFNULL(COLUMN, &quot;DEFAULT_VALUE&quot;) FROM TABLE</code></p>
<p> IFNULL의 첫 번째 인자의 컬럼이 NULL이라면 두 번째 인자의 값을 조회한다.</p>
</li>
</ol>
<ol start="2">
<li><p>COALESCE</p>
<p> COALESCE는 IFNULL처럼 사용할 수 있지만 추가적인 기능을 지원한다.
 <code>SELECT COALESCE(COLUMN1, &quot;DEFAULT_VALUE&quot;) FROM TABLE</code>
 위의 코드는 IFNULL을 사용할 때와 같게 동작한다.</p>
<p> <code>SELECT COALESCE(COLUMN1, COLUMN2, COLUMN3, ...) FROM TABLE</code>
 이렇게 작성 하면, 전달인자의 순서대로 순회하며 NULL이 아닌 값이 나올때 까지 조회하다 NULL이 아닌 인자값을 반환한다.</p>
</li>
<li><p>CASE
 해당 컬럼에 조건식을 사용해 True, False를 판별 후 해당 조건의 로직을 수행한다.</p>
<pre><code class="language-sql"> SELECT
    CASE 
         WHEN COLUMN IS NULL THEN &quot;default_value&quot;
         ELSE COLUMN // 조건식에 해당하는 경우가 없을 시 수행
     END AS COLUMN
    FROM TABLE</code></pre>
<p> 반드시 ELSE문을 작성.</p>
</li>
</ol>
]]></description>
        </item>
    </channel>
</rss>