<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>상형 님의 블로그</title>
    <link>https://tkdgud.tistory.com/</link>
    <description>상형 님의 블로그 입니다.</description>
    <language>ko</language>
    <pubDate>Sun, 11 Oct 2026 03:41:46 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>tkdgud</managingEditor>
    <item>
      <title>[Tracker 배포 회고 6]  배포와 트러블슈팅 정리</title>
      <link>https://tkdgud.tistory.com/23</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5편에서 E2.1.Micro 서버와 Autonomous Database 환경에 실제 서비스를 배포했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;깃허브 저장소에서 코드를 받아오고 Docker 기반으로 애플리케이션을 빌드, 실행하는 과정에서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만난 문제들을 해결했던 과정들을 정리하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. env.prod로&amp;nbsp; 운영 환경변수 분리하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker-compose.prod.yml은 DB접속 정보( 아이디와 비밀번호 등) 을 코드에 직접적으로 넣지않고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경변수로 받도록 설계했다. Autonomous DB이 TLS 접속 문자열의 형태는 이런형식인데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB_URL=jdbc:oracle:thin:@(description=(retry_count=20)(retry_delay=3)(address=(protocol=tcps)(port=1521)(host=&amp;lt;호스트&amp;gt;))(connect_data=(service_name=&amp;lt;서비스명&amp;gt;))(security=(ssl_server_dn_match=yes))) DB_USERNAME=TRACKER DB_PASSWORD=&amp;lt;비밀번호&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 파일은 git에 커밋하지않고 서버에 SSH로 접속한 상태에서 nano(리눅스 텍스트 편집기)로 직접 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker Compose는 --env-file 옵션으로 이 값을 읽어서 컨테이너 안에 주입한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;docker compose -f docker-compose.prod.yml --env-file .env.prod up -d --build&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 방화벽은 두 겹으로 열어야 한다.&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;80번 포트로 접속이 안 될 때 처음엔 당황했는데, 찾아보니 Oracle Cloud는 방화벽이&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 레이어로 되어있는걸 알게되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1번은 OCI Security List (클라우드 네트워크 레벨) 으로 VCN의 Security List에서 Ingress Rule을 추가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2번은 서버내부 iptables(OS 레벨)로 Ubuntu안에도 별도의 방화벽 규칙이 있어서 이것도 따로 열어야했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sudo&amp;nbsp;iptables&amp;nbsp;-L&amp;nbsp;INPUT&amp;nbsp;-n&amp;nbsp;--line-numbers&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;현재&amp;nbsp;규칙&amp;nbsp;확인 &lt;br /&gt;sudo&amp;nbsp;iptables&amp;nbsp;-I&amp;nbsp;INPUT&amp;nbsp;5&amp;nbsp;-m&amp;nbsp;state&amp;nbsp;--state&amp;nbsp;NEW&amp;nbsp;-p&amp;nbsp;tcp&amp;nbsp;--dport&amp;nbsp;80&amp;nbsp;-j&amp;nbsp;ACCEPT&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;REJECT&amp;nbsp;규칙보다&amp;nbsp;앞에&amp;nbsp;삽입 &lt;br /&gt;sudo&amp;nbsp;netfilter-persistent&amp;nbsp;save&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;재부팅해도&amp;nbsp;유지되도록&amp;nbsp;저장&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘중 하나만 열어서는 접속이 안되고 두곳 모두 다 열어야 뚫렸다. (아래는 각각 클라우드 네트워크 레벨에서 80번 포트 Ingress Rule을 추가한 화면과, OS 레벨 Ubuntu에서 80번 포트 ACCEPT 규칙을 추가한 화면이다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;950&quot; data-origin-height=&quot;317&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3hI1e/dJMcaaNxAci/TCohZm908W1PK6fEkh5KU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3hI1e/dJMcaaNxAci/TCohZm908W1PK6fEkh5KU0/img.png&quot; data-alt=&quot;클라우드 네트워크 레벨에서 80번 포트에 Ingress Rule 추가&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3hI1e/dJMcaaNxAci/TCohZm908W1PK6fEkh5KU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3hI1e%2FdJMcaaNxAci%2FTCohZm908W1PK6fEkh5KU0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;653&quot; height=&quot;218&quot; data-origin-width=&quot;950&quot; data-origin-height=&quot;317&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;클라우드 네트워크 레벨에서 80번 포트에 Ingress Rule 추가&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;813&quot; data-origin-height=&quot;155&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Tapf7/dJMcabr4zLJ/xMibQc6Ja14GNsbfMXH0lk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Tapf7/dJMcabr4zLJ/xMibQc6Ja14GNsbfMXH0lk/img.png&quot; data-alt=&quot;OS레벨 Ubuntu에서 80번 포트에 ACCEPT규칙 추가&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Tapf7/dJMcabr4zLJ/xMibQc6Ja14GNsbfMXH0lk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTapf7%2FdJMcabr4zLJ%2FxMibQc6Ja14GNsbfMXH0lk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;661&quot; height=&quot;126&quot; data-origin-width=&quot;813&quot; data-origin-height=&quot;155&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;OS레벨 Ubuntu에서 80번 포트에 ACCEPT규칙 추가&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. 1GB RAM으로 빌드하기 - 스왑 메모리로 버티기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;E2.1.Micro 는 RAM이 954MB 밖에 안되는데, SpringBoot와 React 를 컨테이너 안에서 직접&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드해야했다. 빌드 중간에 메모리 부족으로 죽을 위험이 커서 미리 2GB 스왑 파일을 만들어서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제를 해결할수있는 안전장치를 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sudo&amp;nbsp;fallocate&amp;nbsp;-l&amp;nbsp;2G&amp;nbsp;/swapfile &lt;br /&gt;sudo&amp;nbsp;chmod&amp;nbsp;600&amp;nbsp;/swapfile &lt;br /&gt;sudo&amp;nbsp;mkswap&amp;nbsp;/swapfile &lt;br /&gt;sudo&amp;nbsp;swapon&amp;nbsp;/swapfile &lt;br /&gt;echo&amp;nbsp;'/swapfile&amp;nbsp;none&amp;nbsp;swap&amp;nbsp;sw&amp;nbsp;0&amp;nbsp;0'&amp;nbsp;|&amp;nbsp;sudo&amp;nbsp;tee&amp;nbsp;-a&amp;nbsp;/etc/fstab&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;790&quot; data-origin-height=&quot;85&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lMbAL/dJMcahsoxsx/fpCVFVQhaMiPHBoBy6VJkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lMbAL/dJMcahsoxsx/fpCVFVQhaMiPHBoBy6VJkK/img.png&quot; data-alt=&quot;before&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lMbAL/dJMcahsoxsx/fpCVFVQhaMiPHBoBy6VJkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlMbAL%2FdJMcahsoxsx%2FfpCVFVQhaMiPHBoBy6VJkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;790&quot; height=&quot;85&quot; data-origin-width=&quot;790&quot; data-origin-height=&quot;85&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;before&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1017&quot; data-origin-height=&quot;107&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kQiR7/dJMcacLplej/oQrdM0kqj8eWGfpE5996a0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kQiR7/dJMcacLplej/oQrdM0kqj8eWGfpE5996a0/img.png&quot; data-alt=&quot;after&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kQiR7/dJMcacLplej/oQrdM0kqj8eWGfpE5996a0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkQiR7%2FdJMcacLplej%2FoQrdM0kqj8eWGfpE5996a0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;794&quot; height=&quot;84&quot; data-origin-width=&quot;1017&quot; data-origin-height=&quot;107&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;after&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스왑은 디스크 공간을 비상용 여분 RAM처럼 쓰는 개념이라 진짜 RAM보다 느리지만&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드가 중간에 강제 종료되는 문제를 해결할수있어서 끝까지 완료되는게&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;훨씬 나은 선택이라고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. 백엔드가 계속 재시작한다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;빌드는 무사히 끝났고 컨테이너도 정상적으로 뜬거같은데 docker ps로 현재 상태를 확인해보니&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;좀 이상한 점이있었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;988&quot; data-origin-height=&quot;135&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmgBMx/dJMcafVCWpH/hI80BqBdBbzknV8lklAZb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmgBMx/dJMcafVCWpH/hI80BqBdBbzknV8lklAZb0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmgBMx/dJMcafVCWpH/hI80BqBdBbzknV8lklAZb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcmgBMx%2FdJMcafVCWpH%2FhI80BqBdBbzknV8lklAZb0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;988&quot; height=&quot;135&quot; data-origin-width=&quot;988&quot; data-origin-height=&quot;135&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성된지 4시간이 지났는데 Up 10seconds, 즉 4시간 내에 죽었다 살아나기를 반복하고 있는 상태였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그를 뽑아보니 원인이 나왔는데.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;943&quot; data-origin-height=&quot;188&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/duuZnd/dJMcagfPnFN/mcpnJkZT9c277Uy2D6Fzik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/duuZnd/dJMcagfPnFN/mcpnJkZT9c277Uy2D6Fzik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/duuZnd/dJMcagfPnFN/mcpnJkZT9c277Uy2D6Fzik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FduuZnd%2FdJMcagfPnFN%2FmcpnJkZT9c277Uy2D6Fzik%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;669&quot; height=&quot;133&quot; data-origin-width=&quot;943&quot; data-origin-height=&quot;188&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그건 바로 전에 Autonomous DB에서 작업할때에 로그인 실패가 누적되면서 계정이 자동으로 잠겼던것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 뒤에 비밀번호를 올바로 재설정했었지만 비밀번호 변경과 잠금 해제는 별개의 작업이라 잠금은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그대로 남아있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배포된앱이 컨테이너가 재시작될때마다 로그인을 시도했고, 잠긴 계정이라 계속 ORA-28000을 보내면서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재시작 루프에 빠졌던것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;5. DB 계정 잠금 해결&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그에서 ORA-28000 을 확인했고 Autonomous Database에서 TRACKER 계정이 잠겨있는걸 확인했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ALTER USER TRACKER ACCOUNT UNLOCK;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;954&quot; data-origin-height=&quot;545&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTK6SW/dJMcacYPwoS/hNTz6LjdUWS4w7zSfKPLcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTK6SW/dJMcacYPwoS/hNTz6LjdUWS4w7zSfKPLcK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTK6SW/dJMcacYPwoS/hNTz6LjdUWS4w7zSfKPLcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTK6SW%2FdJMcacYPwoS%2FhNTz6LjdUWS4w7zSfKPLcK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;397&quot; height=&quot;545&quot; data-origin-width=&quot;954&quot; data-origin-height=&quot;545&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계정 잠금을 해제한 뒤 서버의 .env.prod에 설정된 비밀번호도 확인했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 컨테이너를 재생성해서 변경된 환경변수를 다시 적용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;6. 최종 배포 성공 및 검증&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그 마지막줄에서 하는 메시지가 나오면서 최종적으로 배포가 성공했다는것을 알게되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HikariPool-1&amp;nbsp;-&amp;nbsp;Added&amp;nbsp;connection&amp;nbsp;oracle.jdbc.driver.T4CConnection@... &lt;br /&gt;HikariPool-1&amp;nbsp;-&amp;nbsp;Start&amp;nbsp;completed. &lt;br /&gt;[DemoDataSeeder]&amp;nbsp;오늘(...)&amp;nbsp;기준&amp;nbsp;데모&amp;nbsp;데이터&amp;nbsp;32건&amp;nbsp;생성&amp;nbsp;완료&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1016&quot; data-origin-height=&quot;123&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NJmhP/dJMcadjeeHD/KxESTjnNyDCdztixKJsoe1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NJmhP/dJMcadjeeHD/KxESTjnNyDCdztixKJsoe1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NJmhP/dJMcadjeeHD/KxESTjnNyDCdztixKJsoe1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNJmhP%2FdJMcadjeeHD%2FKxESTjnNyDCdztixKJsoe1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1016&quot; height=&quot;123&quot; data-origin-width=&quot;1016&quot; data-origin-height=&quot;123&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker ps로 재확인했을때도 재시작없이 Up상태를 유지했고 브라우저에서 서버 IP주소로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;접속하니 로그인 화면부터 데모데이터까지 잘보이는것을 확인했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드디어 진짜 배포가 끝났다는것을 실감했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;7. 배운점과 느낀점&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 배포에서 가장 크게 느낀거는 배포과정이 생각보다 훨씬 어렵고, 그 과정속에서 문제를 해결하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나씩 나아가는 경험이 굉장히 소중했고 배운게 많았다는 점이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를들어&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 인프라 용량문제 때문에 재시도만 기다리는 것이 아니라, 포트폴리오 일정에 맞춰 아키텍처를 재검토&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하는것&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 네트워크 클라우드 방화벽과 OS 방화벽등 방화벽이 여러 레이어로 구성될수 있다는것&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 계정잠금 이나 비밀번호 정책처럼 눈에 잘 안띄는 보안정책도&amp;nbsp; 장애의 실제 원인이 될수 있다는것,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 에러메시지를 끝까지 확인하고 정확한 원인을 찾아가는 과정이 중요하다는것&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음에 새 프로젝트를 배포할때에 이번에 겪은 문제들을 한번씩 숙지하고 배포를 하면 제대로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대비가 되겠다는 생각이되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;배포된 서비스&lt;/b&gt;: &lt;a href=&quot;http://168.110.18.108&quot;&gt;http://168.110.18.108&lt;/a&gt; (데모 계정: testuser / 1234)&lt;br /&gt;&lt;b&gt;저장소&lt;/b&gt;: &lt;a href=&quot;https://github.com/eblish159/task-tracker&quot;&gt;https://github.com/eblish159/task-tracker&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>tkdgud</author>
      <guid isPermaLink="true">https://tkdgud.tistory.com/23</guid>
      <comments>https://tkdgud.tistory.com/23#entry23comment</comments>
      <pubDate>Thu, 10 Sep 2026 16:56:11 +0900</pubDate>
    </item>
    <item>
      <title>[Tracker 배포 회고 5] A1.Flex 용량 대기, 아키텍처 전환</title>
      <link>https://tkdgud.tistory.com/22</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난글에서 썼듯이 원래배포는 Oracle Cloud Always Free A1.Flex(Ampere ARM) 인스턴스와&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker Compose로 백엔드, 프론트엔드 , Oracle DB컨테이너 모두 하나의 VM으로 운영하려고 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;근데 문제가 생겼는데&lt;/b&gt;. 로컬에서는 이미 잘 돌아가고있기 때문에 클라우드에 옮기기만 하면될줄 알았는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Out of capacity 라는 에러가 반복적으로 계속나왔다.&amp;nbsp; 이 에러를 알아보니 해&lt;b&gt;당 리전에 A1.Flex를 배치할 물리적 호스트 용량이&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;부족하다는 의미였다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 지정한 홈 리전이 인기가 많은 리전이라서 장기간 서버를 배당받지 못할 확률이 높았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 포트폴리오 마감을 생각해서 아키텍쳐, &lt;b&gt;즉 기존계획을 바꾸기로했다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재시도 방법과 다른 리전에서 시도하는 방법은 너무오래걸릴수도있고, 다른 리전에서 시도하는것은 제약이 있어&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선택할수가없었다.&amp;nbsp; 그래서 아키텍쳐를 바꾸기로 정하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; VM.Standard.E2.1.Micro &amp;mdash; x86, 1/8 OCPU, 약 1GB RAM&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; Oracle Autonomous Database &amp;mdash;&amp;nbsp; Oracle이 관리하는 DB 서비스&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 구성하기로했다. DB를 VM에서 분리하고 애플리케이션만 VM에 올리는 구조다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;이 구성의 장점과 단점&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;E2.1.Micro는 RAM이 약 1GB, CPU도 1/8 OCPU라 Spring Boot와 React를 컨테이너 안에서 직접 빌드하기에 부족했다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB를 Autonomous Database로 분리하면서&amp;nbsp; &lt;b&gt;&lt;span&gt;VM의 자원을 애플리케이션에 집중할 수 있다는 장점&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;이 있었다.&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;로컬에서는 Docker Oracle DB까지 실행하지만 운영환경에서는 Autonomous Database를 사용하기때문에&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;기존 docker-compose.yml과 별개로 운영환경을 위한 docker-compose.prod.yml을 따로 분리해서 관리하기로했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;394&quot; data-start=&quot;368&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Autonomous Database 만들기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-end=&quot;484&quot; data-start=&quot;399&quot; data-ke-size=&quot;size16&quot;&gt;OCI 콘솔에서 Autonomous Database를 생성하고 Transaction Processing 워크로드와 Always Free 옵션을 적용했다.&lt;/p&gt;
&lt;p data-end=&quot;484&quot; data-start=&quot;399&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;555&quot; data-start=&quot;489&quot; data-ke-size=&quot;size16&quot;&gt;또한 애플리케이션에서 관리자 계정을 직접 사용하지 않도록 TRACKER라는 별도의 앱 전용 DB 사용자를 생성했다.&lt;/p&gt;
&lt;p data-end=&quot;555&quot; data-start=&quot;489&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;630&quot; data-start=&quot;560&quot; data-ke-size=&quot;size16&quot;&gt;JDBC에서는 Wallet 파일 없이 TLS 방식으로 연결하도록 설정했고, 이후 애플리케이션에서 사용할 접속 정보를 확인했다.&lt;/p&gt;
&lt;p data-end=&quot;687&quot; data-start=&quot;635&quot; data-ke-size=&quot;size16&quot;&gt;이 과정에서 Autonomous Database의 계정 및 비밀번호 정책도 확인할 수 있었다.&lt;/p&gt;
&lt;p data-end=&quot;687&quot; data-start=&quot;635&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;687&quot; data-start=&quot;635&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;E2.1.Micro 생성 완료&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB를 준비한 뒤 애플리케이션을 올릴 VM을 생성했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Shape : VM.Standard.E2.1.Micro&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Image: Ubuntu 24.04 LTS&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Public IPv4 할당&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSH 공개키 등록&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 VCN/서브넷 재사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;느낀점 마무리&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A1.Flex에서 며칠째계속 Out of host capacity만 보고있었는데 E2.1.Micro는 생성요청을 누르자마자 바로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만들어졌다. 며칠을 기다려도 안잡히던 셰이프와 바로잡혀서 되는 셰이프의 차이를 직접 겪으니&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확실히 체감이됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 DB준비와 운영환경의 기본적인 인프라 구성이 완료됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 6편에서는 실제서버에 Docker 프로젝트 올리고 서비스를 실행하는 과정 등 실제 배포과정에서 겪었던&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트러블 슈팅을 정리해보겠다.&lt;/p&gt;</description>
      <author>tkdgud</author>
      <guid isPermaLink="true">https://tkdgud.tistory.com/22</guid>
      <comments>https://tkdgud.tistory.com/22#entry22comment</comments>
      <pubDate>Tue, 8 Sep 2026 18:40:36 +0900</pubDate>
    </item>
    <item>
      <title>[Tracker 배포 회고 4] 로컬에서 docker compose up 성공하기</title>
      <link>https://tkdgud.tistory.com/21</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 글에서는 애플리케이션 코드만으로는 재현할 수 없었던 DB스키마를 Mybatis 매퍼와 실제 Oracle DB를 대조해가며&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQL 파일로 복원하는 과정을 정리했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 그렇게 준비한 Dockerfile, docker-compose.yml, 스키마/시드 SQL 을 로컬 PC에서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 docekr compose up을 한번 돌려보는 과정을 정리한다. 순서대로 하나씩 문제를 풀어나가는 과정을 정리했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;문제 1. Docker Desktop이 아예 안 키지는 문제 ( BIOS AMD SVM )&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker Desktop을 설치하고 실행했는데 가상화 관련 오류로 아예 켜지지않았다. Docker Desktop은 WSL2 기반으로 동작하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WSL2는 다시 하드웨어 가상화 기능이 필요한데, 내 PC는 BIOS에서 AMD의 가상화기능(SVM)이 꺼져있는 상태였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;601&quot; data-origin-height=&quot;375&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RMW0l/dJMcag00Vfn/U9hZwgyn6HTGCsf09ALkhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RMW0l/dJMcag00Vfn/U9hZwgyn6HTGCsf09ALkhk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RMW0l/dJMcag00Vfn/U9hZwgyn6HTGCsf09ALkhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRMW0l%2FdJMcag00Vfn%2FU9hZwgyn6HTGCsf09ALkhk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;539&quot; height=&quot;336&quot; data-origin-width=&quot;601&quot; data-origin-height=&quot;375&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BIOS로 들어가서 SVM Mode를 Enabled로 바꾸고 나서 문제가 해결됐다. 개발PC에서는 신경쓸일이 없던 설정이라,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker를 쓰려면 OS 레벨이아니라 하드웨어 레벨에서부터 가상화가 필요하다는 걸 이번에 처음 체감했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;문제 2. WSL2 설치가 wsl --install 에서 멈춘다&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BIOS 설정을 바꾼다음에 WSL2 자체가 설치되어 있지않아서 wsl --install 을 실행했는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치 과정이 특정단계에서&amp;nbsp;계속 멈추는 상황이 발생했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;577&quot; data-origin-height=&quot;271&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cq8OPy/dJMcafOMk5m/H6yS4bk7VicTCVxs0bB0l0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cq8OPy/dJMcafOMk5m/H6yS4bk7VicTCVxs0bB0l0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cq8OPy/dJMcafOMk5m/H6yS4bk7VicTCVxs0bB0l0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcq8OPy%2FdJMcafOMk5m%2FH6yS4bk7VicTCVxs0bB0l0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;577&quot; height=&quot;271&quot; data-origin-width=&quot;577&quot; data-origin-height=&quot;271&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;position: absolute;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;찾아보니 wsl --install 이 자동으로 켜줘야 할 Windows 선택적 기능(&quot;Linux용 Windows 하위시스템&quot;, &quot;가상 머신 플랫폼&quot;) 이&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 활성화 되지않는 상태에서 커널 업데이트 페키지부터 실행하려다보니 생긴문제였다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제어판의 windows 기능 켜기/끄기에서 두기능을체크했고, dism /online / get-featureinfo 두 기능의 상태가 사용으로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바뀐것을 확인했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이상태에서 다시 wsl --install을 실행하니&amp;nbsp; 정상적으로 설치가 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;622&quot; data-origin-height=&quot;378&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1yI2O/dJMcadXE4Qu/uzGWe7PK1GuXH9Xd9mONdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1yI2O/dJMcadXE4Qu/uzGWe7PK1GuXH9Xd9mONdK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1yI2O/dJMcadXE4Qu/uzGWe7PK1GuXH9Xd9mONdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1yI2O%2FdJMcadXE4Qu%2FuzGWe7PK1GuXH9Xd9mONdK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;622&quot; height=&quot;378&quot; data-origin-width=&quot;622&quot; data-origin-height=&quot;378&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제3. DB포트 1521 충돌&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WSL2 문제를 해결하고 docker compose up 을 실행했더니 이번엔 Oracle DB 컨테이너가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포트 바인딩에 실패했다. 원인은 전에 로컬에서 쓰던 1521포트가 컨테이너의 OracleXE 포트도 1521이라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;충돌한 것이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;915&quot; data-origin-height=&quot;345&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/W0mCD/dJMcajcqd8E/ikEG2Z1d68hiC4QeDI8ZA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/W0mCD/dJMcajcqd8E/ikEG2Z1d68hiC4QeDI8ZA0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/W0mCD/dJMcajcqd8E/ikEG2Z1d68hiC4QeDI8ZA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FW0mCD%2FdJMcajcqd8E%2FikEG2Z1d68hiC4QeDI8ZA0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;915&quot; height=&quot;345&quot; data-origin-width=&quot;915&quot; data-origin-height=&quot;345&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호스트쪽 포트를 1522바꾸는 걸로 해결했다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker-compose.yml&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;oracle-db:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; ports:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;- &quot;1522:1521&quot;&amp;nbsp; &amp;nbsp; &amp;lt;-- 수정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제 4. Oracle 컨테이너가 계속 죽었다가 반복 ( ORA - 01157 )&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포트까지 정리하고 나니 이번엔 Oracle 컨테이너가 뜨는 것 같다가 ORA-01157 에러를 내면서 계속 재시작을 반복하는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제가 생겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;279&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btG5t0/dJMcabyHAQJ/onA2kDdvsSy4TVaYvvcsRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btG5t0/dJMcabyHAQJ/onA2kDdvsSy4TVaYvvcsRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btG5t0/dJMcabyHAQJ/onA2kDdvsSy4TVaYvvcsRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtG5t0%2FdJMcabyHAQJ%2FonA2kDdvsSy4TVaYvvcsRk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;279&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;279&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정확한 내부원인은 끝까지 특정하지 못했지만, -faststarts 태그가 걸려있던 이미지를 gvenzl/oracle-xe:21-slim을 faststart&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;을빼자 더이상 재현되지않고 정상적으로 초기화됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;문제 5. 로그인이 안된다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB까지 정상적으로 뜨고 스키마/ 시드 SQL도 실행했는데, 정작 데모계정 testuser/1234 로 시도하니 로그인 정보가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;없습니다라고 에러가났다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;351&quot; data-origin-height=&quot;252&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xKzlG/dJMcaas8D5u/Nt2eNYpZzF0keghKLc0btk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xKzlG/dJMcaas8D5u/Nt2eNYpZzF0keghKLc0btk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xKzlG/dJMcaas8D5u/Nt2eNYpZzF0keghKLc0btk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxKzlG%2FdJMcaas8D5u%2FNt2eNYpZzF0keghKLc0btk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;351&quot; height=&quot;252&quot; data-origin-width=&quot;351&quot; data-origin-height=&quot;252&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB에 직접 접속해서 확인해보니 USERS.USER_PASSWORD에 저장된 BCrpt 해시가 실제로 1234를 해싱한 값과&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맞지않는 값이었다. sqlplus로 접속해서 새로 생성한 BCrypt해시로 직접 UPDATE해서 해결했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로 update에 떠있는 DB도 고쳤지만 02_seed.sql에도 해시값을 맞게 바꿔서 나중에 볼륨을 초기화해도 같은 문제가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재발하지 않도록 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;603&quot; data-origin-height=&quot;137&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xjXLw/dJMcajcqg9m/olMgOa5qr4GpkQiNOz2RK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xjXLw/dJMcajcqg9m/olMgOa5qr4GpkQiNOz2RK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xjXLw/dJMcajcqg9m/olMgOa5qr4GpkQiNOz2RK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxjXLw%2FdJMcajcqg9m%2FolMgOa5qr4GpkQiNOz2RK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;603&quot; height=&quot;137&quot; data-origin-width=&quot;603&quot; data-origin-height=&quot;137&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sqlplus 재접속시 서비스명을 빼먹고 실행한적이있는데 그러면 CDB$ROOT에 붙어버려서 TRACKER 스키마 자체가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안보이는 문제도있었다 UPDATE가 ORA-00942: table or view does not exist 에러로 실패해서 처음엔 당황했는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SHOWCON_NAME으로 확인해보니 CDB$ROOT에붙어있는걸 확인했다. 서비스명을 제대로 @XEPDB1을 붙여서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 접속하니 해결됐다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;451&quot; data-origin-height=&quot;124&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cK0Fuf/dJMcacRRIQ6/q2XInnoVl9Ev2KX2Mfv0wK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cK0Fuf/dJMcacRRIQ6/q2XInnoVl9Ev2KX2Mfv0wK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cK0Fuf/dJMcacRRIQ6/q2XInnoVl9Ev2KX2Mfv0wK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcK0Fuf%2FdJMcacRRIQ6%2Fq2XInnoVl9Ev2KX2Mfv0wK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;451&quot; height=&quot;124&quot; data-origin-width=&quot;451&quot; data-origin-height=&quot;124&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;문제 6. 로그인은 되는데 Dashboard가 전부 0으로 나온다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인까지는 성공했는데, 이번엔 Dashboard 화면의 모든 통계가 0으로 나왔다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;616&quot; data-origin-height=&quot;404&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VtomH/dJMcabli8NN/72eauqreEYZcqeDPHBPYlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VtomH/dJMcabli8NN/72eauqreEYZcqeDPHBPYlk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VtomH/dJMcabli8NN/72eauqreEYZcqeDPHBPYlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVtomH%2FdJMcabli8NN%2F72eauqreEYZcqeDPHBPYlk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;616&quot; height=&quot;404&quot; data-origin-width=&quot;616&quot; data-origin-height=&quot;404&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터가 아예 없는건 아니었는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원인은 DemoDataSeeder가 실행되지않은것으로 추청했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DemoDataSeeder는 @Profile(&quot;seed&quot;)로 지정되어있는데 컨테이너는 SPRING_PROFILES_ACTIVE=prod&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만 활성화된 상태로 떠 있어서 시더 자체가 동작하지않았던 것이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker-composer.yml&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;backend:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; enviroment:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; SPRING_PROFILES_ACTIVE: prod, seed&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로파일은 prod, seed로 바꾸고 다시 올리니 데모 데이터가 정상적으로 채워지고 Dashboard 수치도 제대로 나왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;마무리&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여기까지 정리하고 나서 다시 docker compose up -d를 실행하니 , 전체 컨테이너가 모두 healthy로 뜨고&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;로그인 데모데이터 모두 정상적으로 동작했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1583&quot; data-origin-height=&quot;258&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NAfOm/dJMcajcrHEa/4XTmyNKYn2RrVahTAMXpa0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NAfOm/dJMcajcrHEa/4XTmyNKYn2RrVahTAMXpa0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NAfOm/dJMcajcrHEa/4XTmyNKYn2RrVahTAMXpa0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNAfOm%2FdJMcajcrHEa%2F4XTmyNKYn2RrVahTAMXpa0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1583&quot; height=&quot;258&quot; data-origin-width=&quot;1583&quot; data-origin-height=&quot;258&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;느낀점&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제가 계층별로 하나씩 드러났다. 처음에는 하드웨어 가상화(BIOIS) -&amp;gt; WSL2 -&amp;gt; Docker 네트워크/포트 -&amp;gt; 이미지 초기화 -&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 적합성 -&amp;gt; Spring 프로파일 설정 순으로 , 순서대로 밑에문제를 해결해야 그 다음 계층의 문제가 보이는 식이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 docker compose down -v가 컨테이너뿐 아니라 볼륨까지 지운다는걸 알고나니 DB 관련 수정은 지금 떠 있는 DB 뿐 아니라 시드 SQL 에도 같이 반영해둬야 나중에 볼륨을 초기화해도 같은 문제가 재발하지않는다는것도 배웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Oracle Cloud에 실제로 배포할 때도 면접관이 접속했을때 데모데이터가 바로 보여야하기때문에 seed프로파일은 계속 켜둔채로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배포하기로 했다. DemoDateSeeder는 실행될때마다 기존 데이터를 지우고 다시 채우는 방식으로 만들워뒀기 때문에,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너가 재시작될때마다 그시점까지 쌓인 데이터까지 함께 초기화된다는 것을 알고 그렇게 진행하기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 편 에서는 이렇게 로컬에서 검증한 구성을 Oracle Cloud Always Free VM에 실제로 올리는 과정을 정리할 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>tkdgud</author>
      <guid isPermaLink="true">https://tkdgud.tistory.com/21</guid>
      <comments>https://tkdgud.tistory.com/21#entry21comment</comments>
      <pubDate>Sat, 5 Sep 2026 13:48:36 +0900</pubDate>
    </item>
    <item>
      <title>[Tracker 배포 회고 3] 존재하지 않던 DB 스키마를 SQL로 복원하기</title>
      <link>https://tkdgud.tistory.com/20</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 글에서는 Dockerfile과 docker-compse.yml을 이용해 백엔드, 프론트엔드 Db컨테이너를 하나의 환경으로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구성하는 과정을 정리했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 그 과정에서&amp;nbsp; &lt;b&gt;DB컨테이너가 처음 실행될 때 필요한 스키마와 &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;초기 데이터를 어떻게 준비했는지&lt;/b&gt; 정리해보려고한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구체적으로는 docker/db/init 폴더에 들어갈 CREATE TABLE 등의 DDL파일과 초기 데이터를&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성하는 과정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 DB 스키마를 새로만들어야 했을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 로컬 환경에서는 몇 달 동안 SQL Developer를 이용해 테이블을 직접생성하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터를 넣어 개발해왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 이 과정에서 &lt;b&gt;테이블을 생성할때 사용했던 CREATE TABLE SQL을 별도의 파일로 보관하지 않았다는 것&lt;/b&gt;이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애플리케이션 코드에는 Mybatis 매퍼와 SQL 쿼리가 남아있었지만 실제 DB 구조를 재현할 수 있는 DDL 파일은 저장소에 존재하지않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 환경에서 Oracle DB 컨테이너를 구상하더라도 기존 로컬 DB의 테이블과 데이터가 자동으로 따라오는것이 아니기 때문에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배포환경에서 동일한 DB구조를 만들기 위해 스키마를 다시 작성해야했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 기존 코드를 기준으로 DB 구조를 역추적해보기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. MyBatis 매퍼를 기준으로 스키마 역추적&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 Mybatis 매퍼 XML을 확인했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;INSERT,SELECT,UPDATE 쿼리에 등장하는 테이블과 컬럼을 하나씩 확인하면서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 컬럼이 존재하고 어떤 용도로 사용되는지 추정했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 TASK 테이블에서 사용하는 컬럼들은 매퍼의 sql을 통해 어느정도 확인할수있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이방법에는 명확한 한계가 있었는데,&amp;nbsp; 매퍼가등장하지않는 컬럼들은 애초에 존재여부를 알수 없었기 떄문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 TASK_CATEGORY 테이블에는 DESCRIPTION, WORK_ROLE 처럼 애플리케이션의 일부 쿼리에서는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용하지 않는 컬럼이 존재했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 매퍼만으로는 다음과 같은 DB 세부정보를 정확하게 알기 어려웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;VARCHAR2&lt;/span&gt;&lt;span&gt;의 실제 길이&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;NOT NULL&lt;/span&gt;&lt;span&gt; 여부&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;DEFAULT&lt;/span&gt;&lt;span&gt; 값&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;IDENTITY&lt;/span&gt;&lt;span&gt; 사용 여부&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;PRIMARY KEY / FOREIGN KEY 제약 조건&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;컬럼의 실제 데이터 타입&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 애플리케이션 코드에서 사용하는 정보와 실제 DB 스키마 전체는 동일하지않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 실제 DB와 대조&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 두번째로 실제 로컬 Oracle DB를 기준으로 확인했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;SQL Developer에서 각 테이블의 컬럼 정의와 제약 조건을 확인하고, 앞에서 MyBatis 매퍼를 통해 추정했던 구조와 하나씩 대조했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정에서 TASK_ID, CATEGORY_ID 가 단순한 NUMBER컬럼이 아니라 GENERATED BY DEFAULT AS IDENTITY를&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용하고 있다는것도 재확인할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 각 VARCHAR2컬럼의 실제 길이와 DEFAULT, NOT NULL 같은 설정을 확인했고, 매퍼에서 사용되지않았던 TASK_CATEGORY 의 컬럼들도 함께 확인했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 실제 DB를 확인한 내용으로 다시 수정해서 CREATE TABLE 문으로 작성했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 기존 로컬 DB에 의존하지않고 SQL파일만으로 동일한 테이블 구조를 생성할수있게 됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;시드 데이터도 함께 준비하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스키마를 복원한 다음에는 애플리케이션이 처음 실행됐을떄 사용할 초기 데이터도 필요했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 02_seed.sql 로 분리했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 부분은 외래키 의존 관계와 로그인 인증방식이다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TASK_CATEGORY.USER_ID 는 USER.USER_ID를 참조하는 외래키 이기때문에&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카데고리를 생성하기전에 먼저 USERS에 데모 계정을 만들어야했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 초기 데이터 역시 테이블 간의 관계를 고려해 실행순서를 정했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 데모계정의 비밀번호를 평문으로 저장하지않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애플리케이션에서는 BCryptPasswordEncoder를 이용해 비밀번호를 검증하고 있기 때문에,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시드 데이터에도 BCrypt로 생성한 해시 값을 저장했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;덕분에 DB 컨테이너가 처음 생성된 이후에도 기존 애플리케이션의 로그인 로직을 사용해 데모 계정으로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인할수있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 DB에 데이터를 넣는것이 아니라&lt;b&gt; 애플리케이션이 실제로 기대하는 데이터형태까지 맞아야했다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이번 작업에서 배운점&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 작업을 통해 코드만으로 데이터베이스 스키마 전체를 복원하는데에는 한계가 있다는것을 확인했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MyBatis 매퍼는 애플리케이션이 실제로 사용하는 테이블과 컬럼을 보여주지만, 사용하지않는 컬럼이나&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정확한 데이터타입, 길이 DEFAULT NOT NULL IDENTITY 같은 DB의 세부설정까지 모두 보장하지는 않는다는걸 알았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 스키마처럼 코드 바깥에 존재하는 정보는 실제DB를 기준으로 확인하고, 이를 재현가능한 SQL 파일로 남겨야했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 작성한 01_schema.sql . 02_seed.sql을 Docker DB초기화 과정에 포함시키면서, 기존 로컬 DB에 의존하지않고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 환경에서도 동일한 DB구조와 초기 데이터를 구성할수 있게됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 경험을 통해 단순히 애플리케이션 코드를 Docker로 묶는 것만으로 배포 환경이 완성되는것은 아니라는 것도 알게됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;애플리케이션이 의존하는 DB 스키마와 초기 데이터까지 코드와 함께 재현할 수 있어야, 동일한 환경을 다시만들수 있다는것을&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;알게되었다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>tkdgud</author>
      <guid isPermaLink="true">https://tkdgud.tistory.com/20</guid>
      <comments>https://tkdgud.tistory.com/20#entry20comment</comments>
      <pubDate>Mon, 31 Aug 2026 20:56:21 +0900</pubDate>
    </item>
    <item>
      <title>[Tracker 배포 회고 2] 멀티스테이지 Dockerfile과 Docker Compose로 세 서비스 엮기</title>
      <link>https://tkdgud.tistory.com/19</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 글에서는 프론트 백엔드 db를 한서버로 묶기로 했는지 그 선택이유를 정리했습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 그 구조를 실제로 어떻게 코드(Dockerfile , docker-composer.yml)로 옮겼는지 정리하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;멀티스테이지 빌드를 쓰는 이유&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백엔드, 프론트엔드 둘 다 빌드할때 필요한 도구와 실행할 때 필요한 것이다릅니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멀티스테이지 빌드는 빌드 환경과 실행 환경을 분리하기 위해 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백엔드: 빌드할때는 Gradle + JDK(소스를 컴파일) 가 필요하지만 , 실행단계에서는 빌드된 jar jre만 있으면 됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드: 빌드할땐&amp;nbsp; Node.js + npm(React 소스를 정적 HTML/JS/CSS로 번들링)이 필요하지만,빌드가 완료되면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 파일이 남기때문에 실행할 땐 그 결과물을 서빙할 Nginx만 있으면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드 도구까지 그대로 최종 이미지에 남겨두면 이미지 용량이 쓸데없이 커지기 떄문에 Docker의 Docker의 멀티스테이지 빌드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;는 이 문제를 해결해줍니다. - 빌드용 스테이지에서 만든 결과물만 실행용 스테이지로 복사해오고, 빌드 스테이지 자체(도구,소스,캐시) 는 최종 이미지에 남기지않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; 백엔드 Dockerfile&lt;/b&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;#&amp;nbsp;----&amp;nbsp;Build&amp;nbsp;stage&amp;nbsp;---- &lt;br /&gt;FROM&amp;nbsp;eclipse-temurin:17-jdk-jammy&amp;nbsp;AS&amp;nbsp;build&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;#JDK가 포함된 이미지에서 시작, 이스테이지에 build 라는 이름을 붙여서&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;#나중에 실행 스테이지에서 copy --from=build로 결과물만 꺼내올수 있게함.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;br /&gt;WORKDIR&amp;nbsp;/app &lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;#컨테이너 안에서 앞으로의 작업 기준 폴더를 /app으로 지정&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;br /&gt;COPY&amp;nbsp;gradlew&amp;nbsp;.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;# 로컬의 gradlew(Gradle Wrapper 실행 스크립트)를 컨테이너의 /app 안으로 복사&lt;br /&gt;COPY&amp;nbsp;gradle&amp;nbsp;gradle&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;# gradle wrapper 동작에 필요한 grale/ 폴더를 복사&lt;br /&gt;COPY&amp;nbsp;build.gradle&amp;nbsp;settings.gradle&amp;nbsp;./&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;#의존성 목록과 프로젝트 설정 복사&lt;br /&gt;COPY&amp;nbsp;src&amp;nbsp;src&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;# 실제 자바 소스코드 전체 복사&lt;br /&gt;&lt;br /&gt;RUN&amp;nbsp;chmod&amp;nbsp;+x&amp;nbsp;gradlew&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;./gradlew&amp;nbsp;bootJar&amp;nbsp;-x&amp;nbsp;test&amp;nbsp;--no-daemon&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;# gradlew에 실행 권한을 준 다음, bootJar 태스크를 실행해서&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;# 모든 의존성이 포함된 실행 가능한 jar 파일을 생성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;# -x test : 테스트는 건너띔 (db 연결이 필요한 테스트라 빌드단계에선 무조건 실패&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;#--no daemon : 컨테이너 안에서는 어차피 매번 새로 빌드되니, 백그라운드 데몬 없이 바로 실행&lt;br /&gt;&lt;br /&gt;#&amp;nbsp;----&amp;nbsp;Runtime&amp;nbsp;stage&amp;nbsp;---- &lt;br /&gt;FROM&amp;nbsp;eclipse-temurin:17-jre-jammy&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;#여기서 부터는 완전히 새로운 이미지 시작. jdk 가 아니라 jre만 있는&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;#가벼운 이미지를 베이지로씀.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;br /&gt;&lt;br /&gt;WORKDIR&amp;nbsp;/app&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;# 마찬가지로 작업 기준 폴더 지정&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;b&gt;COPY&amp;nbsp;--from=build&amp;nbsp;/app/build/libs/*.jar&amp;nbsp;app.jar &lt;br /&gt;# build라는 이름 붙인스테이지의 &lt;b&gt;&amp;nbsp;/app/build/libs/*.jar app.jar파일을&lt;/b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;#이 스테이지로 가져와서 app.jar이라는 이름으로 저장.&amp;nbsp; 소스코드는 가져오지않음.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;br /&gt;ENV&amp;nbsp;SPRING_PROFILES_ACTIVE=prod&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;#컨테이너 실행 시 환경변수 SPRING_PROFILES_ACTIVE=pord 를 기본값으로 심어둠.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;# -&amp;gt; Spring Boot가 application-prod.yaml 설정을 읽도록 만듦.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &lt;br /&gt;EXPOSE&amp;nbsp;18080 &lt;br /&gt;#이 컨테이너가 18080포트를 쓴다는걸 문서화하는 용도&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;br /&gt;ENTRYPOINT&amp;nbsp;[&quot;java&quot;,&amp;nbsp;&quot;-jar&quot;,&amp;nbsp;&quot;app.jar&quot;]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;# 컨테이너가 시작될때 실행할 명령어.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;# java -jar app.jar 를 실행해서 방금 복사해온 jar파일을 구동시킴&lt;/b&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;-x test&lt;/b&gt;로 테스트를 건너 뜁니다 . TrackerApplicationtests는 @SpringBooteTest로 전체 스프링 컨텍스트를 띄우면서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 DB커넥션을 시도합니다. 그런데 Docker 이미지를 빌드하는 시점에는 아직 DB컨테이너가 준비되있지않기 떄문에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드단계에서 테스트를 실행하면 무조건 실패하기떄문에 테스트는 로컬개발중&amp;nbsp; ./gradlew test로 따로 돌리는걸로 하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지 빌드에서 제외했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; eclipse-temurin:17-jdk-jammy &amp;rarr; eclipse-temurin:17-jre-jammy&lt;/b&gt; 로 베이스 이미지를 바꿉니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드 스테이지는 컴파일이 필요하니 jdk, 실행스테이지는 이미 컴파일된 jar만 돌리면 되니 jre로만 충분합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;jdk이미지가 jre이미지보다 훨씬크기때문에 최종이미지를 가볍게 만들도록 작성했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;SPRING_PROFILES_ACTIVE=prod&lt;/b&gt;로 배포용 설정을 활성화합니다. 로컬개발용 application.yaml과는 별도로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;applicatoin-prod.yaml을 만들어서 DB접속 정보를 전부 환경변수로 받도록했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;spring: &lt;br /&gt;&amp;nbsp;&amp;nbsp;datasource: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;url:&amp;nbsp;jdbc:oracle:thin:@${DB_HOST:localhost}:${DB_PORT:1521}/${DB_SERVICE:XEPDB1} &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;username:&amp;nbsp;${DB_USERNAME} &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;password:&amp;nbsp;${DB_PASSWORD}&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 이미지 자체에는 어떤 민감정보도 들어가지않고, 실행할 떄 docker-compose.yml이 환경변수로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주입해 주는값만 읽습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; 프론트엔드 Dockerfile&lt;/b&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;#&amp;nbsp;----&amp;nbsp;Build&amp;nbsp;stage&amp;nbsp;---- &lt;/b&gt;&lt;br /&gt;&lt;b&gt;FROM&amp;nbsp;node:20-alpine&amp;nbsp;AS&amp;nbsp;build &lt;/b&gt;&lt;br /&gt;&lt;b&gt;WORKDIR&amp;nbsp;/app &lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;COPY&amp;nbsp;package*.json&amp;nbsp;./ &lt;/b&gt;&lt;br /&gt;&lt;b&gt;RUN&amp;nbsp;npm&amp;nbsp;ci &lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;COPY&amp;nbsp;.&amp;nbsp;. &lt;/b&gt;&lt;br /&gt;&lt;b&gt;RUN&amp;nbsp;npm&amp;nbsp;run&amp;nbsp;build &lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;#&amp;nbsp;----&amp;nbsp;Runtime&amp;nbsp;stage&amp;nbsp;---- &lt;/b&gt;&lt;br /&gt;&lt;b&gt;FROM&amp;nbsp;nginx:alpine &lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;COPY&amp;nbsp;--from=build&amp;nbsp;/app/dist&amp;nbsp;/usr/share/nginx/html &lt;/b&gt;&lt;br /&gt;&lt;b&gt;COPY&amp;nbsp;nginx.conf&amp;nbsp;/etc/nginx/conf.d/default.conf &lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;EXPOSE&amp;nbsp;80&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; nginx.conf 파일 : 빌드후 컴파일된 결과물로부터 /api 요청은백엔드로 넘겨주고, 그외 나머지 요청은&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;스스로 화면을 내려줍니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;server { &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;listen 80;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;#이 서버가 80번 포트로 들어오는 요청을받음&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;server_name _;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;#특정 도메인 이름을 지정하지않고, 어떤 도메인으로 와도 이 설정을 사용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;root /usr/share/nginx/html;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;#정적 파일(프론트 빌드 결과물)이 있는 위치;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;index index.html;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;# 폴더 경로로 요청이 오면 기본으로 보여줄 파일&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;# 백엔드 api 프록시 (세션 쿠키 그대로 전달)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; location /api/ {&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; #api로 시작하는 요청에 대해서만 규칙 적용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; proxy_pass http://backend:18080/api/;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; proxy_set_header Host $host;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; proxy_set_header X-Real-IP $remote_addr;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; proxy_set_header X-Forwarded-Proto $scheme;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; #원래 요청 정보(어떤 도메인으로 왔는지, 실제 ip가 뭔지 등)을 백엔드 한테도 그대로 전해준다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; proxy_cookie_path / /;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; #백엔드가 내려주는 세션 쿠키가 브라우저한테 제대로 전달되도록 경로를 맞춰줌.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; #(세션 인증이 정상 동작하려면 필요)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;}&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;# React Router(SPA) 클라이언트 라우팅 대응&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;location / {&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;#api 가 아닌 나머지 요청 처리&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;try_files $uri $uri/ /inext.html ;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;#요청 경로에 해당하는 실제 파일이 있으면 그걸주고, 없으면index.html을 줘서 React Router가&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;알아서 화면을 그림. ( 새로고침해도 404가 안뜨게 하는 처리)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;}&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; docker-compose.yml로 세 서비스 엮기 (프론트 , 백엔드 , DB)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker-compose.yml은 DB, 백엔드, 프론트(nginx) 세 컨테이너를 하나로 묶어서, 서로 이름으로 찾아가게 하고, DB가 준비된 다음에 백엔드가 뜨도록 순서를 맞추고, 비밀번호는 따로 분리해둔 파일이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;services: &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;oracle-db: &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;image:&amp;nbsp;gvenzl/oracle-xe:21-slim-faststart &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;Oracle&amp;nbsp;XE를&amp;nbsp;미리&amp;nbsp;만들어둔&amp;nbsp;이미지를&amp;nbsp;그대로&amp;nbsp;가져다&amp;nbsp;씀&amp;nbsp;(우리가&amp;nbsp;직접&amp;nbsp;Dockerfile&amp;nbsp;안&amp;nbsp;만듦) &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;container_name:&amp;nbsp;tracker-db &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;컨테이너&amp;nbsp;이름&amp;nbsp;지정&amp;nbsp;(docker&amp;nbsp;ps&amp;nbsp;등에서&amp;nbsp;보기&amp;nbsp;편하라고) &lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;environment: &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ORACLE_PASSWORD:&amp;nbsp;${ORACLE_SYSTEM_PASSWORD} &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;APP_USER:&amp;nbsp;${DB_USERNAME} &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;APP_USER_PASSWORD:&amp;nbsp;${DB_PASSWORD} &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;.env&amp;nbsp;파일에서&amp;nbsp;값을&amp;nbsp;읽어와서&amp;nbsp;DB&amp;nbsp;계정을&amp;nbsp;만듦&amp;nbsp;&amp;mdash;&amp;nbsp;비밀번호가&amp;nbsp;여기&amp;nbsp;직접&amp;nbsp;안&amp;nbsp;적혀있는&amp;nbsp;게&amp;nbsp;핵심 &lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ports: &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;&quot;1521:1521&quot; &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;필요하면&amp;nbsp;로컬에서&amp;nbsp;SQL&amp;nbsp;클라이언트로&amp;nbsp;직접&amp;nbsp;접속해볼&amp;nbsp;수&amp;nbsp;있게&amp;nbsp;포트&amp;nbsp;열어둠&amp;nbsp;(참고용) &lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;volumes: &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;oracle-data:/opt/oracle/oradata &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;./docker/db/init:/container-entrypoint-initdb.d &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;첫&amp;nbsp;줄:&amp;nbsp;DB&amp;nbsp;데이터를&amp;nbsp;컨테이너&amp;nbsp;밖(볼륨)에&amp;nbsp;보존&amp;nbsp;&amp;mdash;&amp;nbsp;컨테이너&amp;nbsp;지워도&amp;nbsp;데이터는&amp;nbsp;남음&amp;nbsp;(핵심&amp;nbsp;개념) &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;둘째&amp;nbsp;줄:&amp;nbsp;스키마/시드&amp;nbsp;SQL&amp;nbsp;파일을&amp;nbsp;최초&amp;nbsp;1회&amp;nbsp;자동&amp;nbsp;실행되게&amp;nbsp;넣어줌 &lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;healthcheck: &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;test:&amp;nbsp;[&quot;CMD&quot;,&amp;nbsp;&quot;healthcheck.sh&quot;] &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;interval:&amp;nbsp;15s &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;timeout:&amp;nbsp;10s &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;retries:&amp;nbsp;30 &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;start_period:&amp;nbsp;60s &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;DB가&amp;nbsp;&quot;진짜로&amp;nbsp;쿼리&amp;nbsp;받을&amp;nbsp;준비&amp;nbsp;됐는지&quot;&amp;nbsp;주기적으로&amp;nbsp;체크&amp;nbsp;(정확한&amp;nbsp;옵션&amp;nbsp;값은&amp;nbsp;참고만) &lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;restart:&amp;nbsp;unless-stopped &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;컨테이너가&amp;nbsp;죽으면&amp;nbsp;자동으로&amp;nbsp;다시&amp;nbsp;켜라는&amp;nbsp;설정&amp;nbsp;(참고용) &lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;backend: &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;build: &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;context:&amp;nbsp;. &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dockerfile:&amp;nbsp;Dockerfile &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;백엔드는&amp;nbsp;이미지가&amp;nbsp;따로&amp;nbsp;없으니,&amp;nbsp;우리가&amp;nbsp;만든&amp;nbsp;Dockerfile로&amp;nbsp;직접&amp;nbsp;빌드 &lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;container_name:&amp;nbsp;tracker-backend &lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;environment: &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;SPRING_PROFILES_ACTIVE:&amp;nbsp;${SPRING_PROFILES:-prod} &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;DB_HOST:&amp;nbsp;oracle-db &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;DB_PORT:&amp;nbsp;1521 &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;DB_SERVICE:&amp;nbsp;XEPDB1 &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;DB_USERNAME:&amp;nbsp;${DB_USERNAME} &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;DB_PASSWORD:&amp;nbsp;${DB_PASSWORD} &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;DB_HOST가&amp;nbsp;&quot;oracle-db&quot;인&amp;nbsp;게&amp;nbsp;핵심&amp;nbsp;&amp;mdash;&amp;nbsp;컨테이너&amp;nbsp;이름이&amp;nbsp;곧&amp;nbsp;호스트명이&amp;nbsp;된다는&amp;nbsp;원리&amp;nbsp;그대로 &lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;depends_on: &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;oracle-db: &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;condition:&amp;nbsp;service_healthy &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;DB가&amp;nbsp;healthcheck&amp;nbsp;통과할&amp;nbsp;때까지&amp;nbsp;backend를&amp;nbsp;안&amp;nbsp;띄움&amp;nbsp;(핵심:&amp;nbsp;순서&amp;nbsp;보장&amp;nbsp;이유) &lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;restart:&amp;nbsp;unless-stopped &lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;frontend: &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;build: &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;context:&amp;nbsp;./frontend &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dockerfile:&amp;nbsp;Dockerfile &lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;container_name:&amp;nbsp;tracker-frontend &lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ports: &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;&quot;80:80&quot; &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;유일하게&amp;nbsp;외부(호스트)에&amp;nbsp;실제로&amp;nbsp;열어주는&amp;nbsp;포트&amp;nbsp;&amp;mdash;&amp;nbsp;사용자는&amp;nbsp;결국&amp;nbsp;이&amp;nbsp;80번으로&amp;nbsp;접속함 &lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;depends_on: &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;backend &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;backend가&amp;nbsp;먼저&amp;nbsp;뜬&amp;nbsp;다음에&amp;nbsp;frontend를&amp;nbsp;띄움&amp;nbsp;(healthcheck까진&amp;nbsp;아니고&amp;nbsp;단순&amp;nbsp;순서만) &lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;restart:&amp;nbsp;unless-stopped &lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;volumes: &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;oracle-data: &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;#&amp;nbsp;위에서&amp;nbsp;쓴&amp;nbsp;oracle-data&amp;nbsp;볼륨을&amp;nbsp;여기서&amp;nbsp;선언&amp;nbsp;(Docker가&amp;nbsp;관리하는&amp;nbsp;저장&amp;nbsp;공간)&lt;/b&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포트(ports) 가 프론트에만 있는이유 : /api로 오는 요청은 백엔드로 넘기고 그외 요청은 nginx 자기자신이 갖고있는 정적파일 (React 결과물을 ) 스스로 내려주는 일종의 창구 역활을 하기때문에 이 역할을 하는 프론트에만 포트가 존재합니다.&lt;/p&gt;</description>
      <category>하루 코딩</category>
      <author>tkdgud</author>
      <guid isPermaLink="true">https://tkdgud.tistory.com/19</guid>
      <comments>https://tkdgud.tistory.com/19#entry19comment</comments>
      <pubDate>Mon, 31 Aug 2026 19:30:06 +0900</pubDate>
    </item>
    <item>
      <title>[Tracker 배포 회고 1] (배포환경 결정)</title>
      <link>https://tkdgud.tistory.com/18</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;배포 전 상황&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬 개발환경은 이렇게 구성되어 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;프론트엔드&lt;/b&gt;: React + Vite, 개발서버(localhost:5173) 에서 Vite의 프록시설정( /api -&amp;gt; localhost:18080)으로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백엔트를 호출&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;백엔드&lt;/b&gt; : Spring Boot (localhost:18080), 세션 기반 인증 (Spring Security + HttpSession )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;DB&lt;/b&gt;: Oracle XE, 로컬에 직접 설치&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드 코드는 API를 호출 할때 상대 경로를 통해서 보내도록 구성했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fetch(&quot;/api/tasks&quot;,&amp;nbsp;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;credentials:&amp;nbsp;&quot;include&quot;,&amp;nbsp;//&amp;nbsp;세션&amp;nbsp;쿠키를&amp;nbsp;함께&amp;nbsp;보냄 &lt;br /&gt;});&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬에서 Vite 프록시가 알아서 /api를 백엔드로 연결해줘서 문제는 없었지만 배포를 할때에는 이 상대경로가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제가 될수있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;배포 목표&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 포트폴리오로 쓸것이므로&amp;nbsp; 링크 하나로 접속해서 로그인 &amp;gt; 대시보드 &amp;gt; 리포트 까지 확인할수있어야한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 무료로, 그리고 시간제한 없이 상시 켜져있어야한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. DB는 가능하면 로컬에서 쓰던 Oracle을 유지하고싶다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1번 방식 &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;프론트엔드 -&amp;gt; Vercel/ Netlify (정적 호스팅 , 무료, CDN 제공)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;백엔드 + DB -&amp;gt; 별도 서버 (클라우드 VM)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1번 방식의 문제점&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 상대 경로 fetch가 깨진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fetch(&quot;/api/tasks&quot;) 는 &quot;현재 페이지가 열려있는 도메인 + /api/tasks로 요청을 보내는데 프론트가 my-tracker.vercel.app에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;있고, 백엔드가 전혀다른 ip 도메인에 있다면 이 요청은 백엔드에 도달하지않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 절대경로로 고쳐도 세션 쿠기 문제가 남는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트와 백엔드가 서로 다른 도메인이 되는 순간 브라우저 기준으로서 다른 사이트가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;credentials: &quot;include&quot; 로 세션을 주고받으려면 SecurityConfig 에 CORS 설정이 있어야 한다. 즉, 인증구조에 손을 대야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2번방식 (채택)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;그래서&amp;nbsp; 프론트 백엔드 DB를 전부 같은서버 안에서, Nginx가 프론트 정적 파일을 서빙하면서 /api로 시작하는 요청만 백엔드&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;컨테이너로 넘겨주는 구조를 선택했다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조의 장점은 코드를 한줄도 안고쳐도되고, DB를 Oracle로 유지할수있다. 또한 인프라를 코드로 관리할수있어서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker-composer.yml 하나로 정의해두면, 서버를 새로 만들어도 docker compose up 한번으로 동일한 환경을 재현할수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호스팅은 Oracle Cloud의 AlwaysFree티어를 써서 스펙이 넉넉하고 슬립이나 사용시간제한이 없이 상시켜져있게 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;257&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/buJ9DW/dJMcabeecly/kJ0cKckP6jfCSKdxl1kS2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/buJ9DW/dJMcabeecly/kJ0cKckP6jfCSKdxl1kS2k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buJ9DW/dJMcabeecly/kJ0cKckP6jfCSKdxl1kS2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbuJ9DW%2FdJMcabeecly%2FkJ0cKckP6jfCSKdxl1kS2k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;257&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;257&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>하루 코딩</category>
      <author>tkdgud</author>
      <guid isPermaLink="true">https://tkdgud.tistory.com/18</guid>
      <comments>https://tkdgud.tistory.com/18#entry18comment</comments>
      <pubDate>Thu, 27 Aug 2026 15:14:53 +0900</pubDate>
    </item>
    <item>
      <title>[하루 코딩] Rest API란?</title>
      <link>https://tkdgud.tistory.com/17</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 Rest API에 대해서 파헤쳐보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1.Rest API란?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹에서 클라이이언트와 서버가 데이터를 주고받는 통신방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Rest는 &lt;b&gt;Representational State Transfer&lt;/b&gt;의 약자로 HTTP 프로토콜을 기반으로 데이터를&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청하고 응답하는 구조입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 말하면&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드가 백엔드에 데이터를 요청하면 서버가 Json 형태로 데이터를 반환하는 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를들어 제 프로젝트에서&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;GET /api/dashboard?startDate=2026-02-01&amp;amp;endDate=2026-02-22&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게&amp;nbsp; API요청을 보내면&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;totalTaskCount&quot;: 15,
  &quot;completedTaskCount&quot;: 8,
  &quot;activeTaskCount&quot;: 7
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 서버는 JSON 데이터를 반환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 URL을 통해 데이터를 요청하고, JSON형태로 응답하는 구조를 REST API라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 REST API는 단순히 JSON데이터를 주고받는 통신방식만을 의미하지않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;REST API는 몇가지의 중요한 개념을 기반으로 만들어집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;411&quot; data-start=&quot;395&quot;&gt;Resource (리소스)&lt;/li&gt;
&lt;li data-end=&quot;433&quot; data-start=&quot;412&quot;&gt;Representation (표현)&lt;/li&gt;
&lt;li data-end=&quot;458&quot; data-start=&quot;434&quot;&gt;State Transfer (상태 전달)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 세가지 개념이 REST라는 이름의 의미입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Resource&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리소스란 서버에서 관리하는 데이터 , 객체를 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를들어서 사용자정보, 게시글, 상품정보 등이 있는데 REST에서는 이러한 리소스를 URL로 표현합니다.&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;GET /users&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 사용자를 조회하는경우,&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;GET /users/1&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 /users는 사용자라는 Resource이고, /users/1 은 사용자 중에서 id가 1인 리소스를 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 , REST 에서 URL은 데이터를 명사로 표현하고, 동작은 HTTP METHOD가 담당합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;HTTP Method (동작표현)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Rest API에서는 HTTP method를 사용하여 어떤 동작을 할 것인지 표현합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GET&lt;/td&gt;
&lt;td&gt;데이터 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;POST&lt;/td&gt;
&lt;td&gt;데이터 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PUT&lt;/td&gt;
&lt;td&gt;데이터 전체 수정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PATCH&lt;/td&gt;
&lt;td&gt;데이터 일부 수정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DELETE&lt;/td&gt;
&lt;td&gt;데이터 삭제&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GET /users&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;POST&amp;nbsp;/users&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DELETE /users/1 등..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 REST API에서는 URL과 Method를 조합하여 기능을 표현합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Representation&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;REST에서 Representation은 리소스를 어떤 형태로 표현할 것인가를 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 사용자 데이터라는 리소스가 있다고 하면 이렇게 여러 형태로 표현할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSON 형태&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;id&quot;:1,
  &quot;name&quot;:&quot;kim&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;XML 형태&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;user&amp;gt;
  &amp;lt;id&amp;gt;1&amp;lt;/id&amp;gt;
  &amp;lt;name&amp;gt;kim&amp;lt;/name&amp;gt;
&amp;lt;/user&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML 형태&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;h1&amp;gt;kim&amp;lt;/h1&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 REST에서는 실제 데이터 자체가 전달되지않고 리소스의 Representation이 전달됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;State Transfer(상태 전달)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;REST에서 State Transfer은 클라이언트의 상태가 요청을 통해 서버로 전달되는것을 말합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;GET /products?page=2&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 요청이 있다면 page=2가 클라이언트의 상태입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상태 정보가 서버로 전달되어 결과가 만들어집니다.&amp;nbsp; 이처럼&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;REST에서는 상태를 서버에 저장하지않고 &lt;b&gt;요청을 통해 전달하는 방식&lt;/b&gt;을 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 REST API의 특징은&amp;nbsp; &lt;b&gt;요청 하나만으로 처리가 가능&lt;/b&gt;해야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 개념을 &lt;b&gt;Stateless&lt;/b&gt; 라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>하루 코딩</category>
      <author>tkdgud</author>
      <guid isPermaLink="true">https://tkdgud.tistory.com/17</guid>
      <comments>https://tkdgud.tistory.com/17#entry17comment</comments>
      <pubDate>Mon, 2 Mar 2026 18:20:46 +0900</pubDate>
    </item>
    <item>
      <title>[하루 코딩] Spring 에서 new 없이 객체 생성</title>
      <link>https://tkdgud.tistory.com/16</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring 에서 Bean과 생성자 주입에 대해서 배워보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;@RestController &lt;/b&gt;&lt;br /&gt;&lt;b&gt;public class ImController { &lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private final ImService service; &lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public ImController(ImService service) { &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;this.service&amp;nbsp;=&amp;nbsp;service; &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} &lt;/b&gt;&lt;br /&gt;&lt;b&gt;}&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 Spring 에서 쓰는 기본적인 코드입니다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 원래 이 코드는 자바 문법으로 봤을때는 불가능한 코드입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바에서 생성자는 누군가가 직접 호출해야만 실행되는데.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;public ImController(ImService service) { ... }&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 생성자를 호출 하려면&amp;nbsp; 이 코드들이 있어야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ImService service = new ImServiceImpl(); &lt;/b&gt;&lt;br /&gt;&lt;b&gt;ImController controller = &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;new ImController(service);&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바의 기본규칙에서&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체는 new로 직접 생성&lt;/li&gt;
&lt;li&gt;생성자 인자는 누군가 직접 만들어서 전달&lt;/li&gt;
&lt;li&gt;자동 생성, 자동 주입 같은개념 자바에 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 Spring 이 없으면 혼자 동작할 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, Spring에서는 서버가 시작할때 클래스들을 스캔하고 , 특정 어노테이션이 붙은 클래스들을 발견,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 클래스들을 bean 객체로 만들고 보관합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SpringBoot 애플리케이션이 실행되면 어노테이션이 붙은 클래스들을 찾아서 서버 시작 시점에 객체를 미리 생성해둡니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 객체들을 전부 Spring Container 안에 저장이 됩니다.&lt;/p&gt;</description>
      <category>하루 코딩</category>
      <author>tkdgud</author>
      <guid isPermaLink="true">https://tkdgud.tistory.com/16</guid>
      <comments>https://tkdgud.tistory.com/16#entry16comment</comments>
      <pubDate>Mon, 26 Jan 2026 16:06:29 +0900</pubDate>
    </item>
    <item>
      <title>[하루 코딩] 흐름 제어 코드 정리</title>
      <link>https://tkdgud.tistory.com/13</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 Java에서 return, break, continue등 흐름제어 관련 코드를 정리해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. return&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;역할&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메서드 즉시종료한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호출한 곳으로 값을 돌려준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;사용 형태&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;return;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;return 값;&amp;nbsp; &amp;nbsp; &amp;nbsp;이런식으로&amp;nbsp; 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;특징&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;return 아래 코드는 절대 실행 안된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메서드 안에서 여러번 사용 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예제&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;int add(int a, int b){&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;return a+b;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;}&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;void test(int x){&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if(x&amp;lt;0){&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;return;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;}&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;System.out.println(&quot;실행됨&quot;);&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;}&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. break&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;역할&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복문 즉시 종료&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 switch문 종료&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예제&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;for(int&amp;nbsp;i&amp;nbsp;=&amp;nbsp;0;&amp;nbsp;i&amp;nbsp;&amp;lt;&amp;nbsp;10;&amp;nbsp;i++){ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if(i&amp;nbsp;==&amp;nbsp;5){ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;break; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;System.out.println(i); &lt;br /&gt;}&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력-&amp;gt; 0 1 2 3 4&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3.continue&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;역할&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 반복만 건너뛰고 다음반복으로 이동&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예제&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;for(int&amp;nbsp;i=1;&amp;nbsp;i&amp;lt;=5;&amp;nbsp;i++){ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if(i&amp;nbsp;==&amp;nbsp;3){ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;continue; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;System.out.println(i); &lt;br /&gt;}&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력-&amp;gt; 1 2 4 5&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4.break label&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;역할&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 라벨이 붙은 반복문 전체 종료&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;outer: &lt;br /&gt;for(int&amp;nbsp;i&amp;nbsp;=&amp;nbsp;0;&amp;nbsp;i&amp;nbsp;&amp;lt;&amp;nbsp;5;&amp;nbsp;i++){ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for(int&amp;nbsp;j&amp;nbsp;=&amp;nbsp;0;&amp;nbsp;j&amp;nbsp;&amp;lt;&amp;nbsp;5;&amp;nbsp;j++){ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if(j&amp;nbsp;==&amp;nbsp;2){ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;break&amp;nbsp;outer; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} &lt;br /&gt;}&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt;i=0이고 j는 2일떄 종료된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5.continue label&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;역할&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 라벨이 붙은 반복문의 다음 반복으로 이동한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;outer: &lt;br /&gt;for(int&amp;nbsp;i&amp;nbsp;=&amp;nbsp;0;&amp;nbsp;i&amp;nbsp;&amp;lt;&amp;nbsp;3;&amp;nbsp;i++){ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for(int&amp;nbsp;j&amp;nbsp;=&amp;nbsp;0;&amp;nbsp;j&amp;nbsp;&amp;lt;&amp;nbsp;3;&amp;nbsp;j++){ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if(j&amp;nbsp;==&amp;nbsp;1){ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;continue&amp;nbsp;outer; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;System.out.println(i&amp;nbsp;+&amp;nbsp;&quot;&amp;nbsp;&quot;&amp;nbsp;+&amp;nbsp;j); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} &lt;br /&gt;}&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt;출력은 0 0&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 1 0&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 2 0&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>하루 코딩</category>
      <author>tkdgud</author>
      <guid isPermaLink="true">https://tkdgud.tistory.com/13</guid>
      <comments>https://tkdgud.tistory.com/13#entry13comment</comments>
      <pubDate>Fri, 2 Jan 2026 18:52:10 +0900</pubDate>
    </item>
    <item>
      <title>[하루 코딩]진법과 수의 표현</title>
      <link>https://tkdgud.tistory.com/11</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 진법(Base or Redix)이란 ?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 자리에서 표현할 수 있는 숫자의 종류 개수를 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자 한 칸이 늘어날때마다 그 기수(base)만큼 자릿값이 커지는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를들어,&amp;nbsp; 2진법이면 기수는 2이고&amp;nbsp; 사용가능한 숫자는 0과 1이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또,&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 8진법이면 기수는 8이고, 사용가능한 숫자는 0~7 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 자리값(가중치)의 개념&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 수는 자리마다 가중치를 갖는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왼쪽으로 갈수로 기수가 거듭제곱으로 커진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를들어 (1011)의 2진수는&amp;nbsp; &amp;nbsp;1 x 2^3 + 0 x 2^2 + 1 x 2^1 + 1 x 2^0 = 11의 10진수이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소수점 아래쪽은 오른쪽으로 갈수록 음수가 커진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 진법 변환&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 다른 진법 -&amp;gt; 10진법&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 자리의 숫자값 x 가중치 의 합이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식은&amp;nbsp; result = reult x base + value&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시 (2AF)16&amp;nbsp; = 2x16^2 + 10x16 + 15 = 687(10)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자 계산 누적 결과&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 61px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&amp;lsquo;2&amp;rsquo;&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;0&amp;times;16+2&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&amp;lsquo;A&amp;rsquo;&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;2&amp;times;16+10&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;42&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&amp;lsquo;F&amp;rsquo;&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;42&amp;times;16+15&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&lt;b&gt;687&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 value값은 문자를 16진수로 변환한값이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;--------------------------------------------------------------------------------------------------------&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;진법 변환식 (백준 문제 관련)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;import&amp;nbsp;java.util.*; &lt;br /&gt;&lt;br /&gt;public&amp;nbsp;class&amp;nbsp;Main&amp;nbsp;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;static&amp;nbsp;void&amp;nbsp;main(String[]&amp;nbsp;args)&amp;nbsp;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Scanner&amp;nbsp;sc&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Scanner(System.in); &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String&amp;nbsp;s&amp;nbsp;=&amp;nbsp;sc.next(); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int&amp;nbsp;base&amp;nbsp;=&amp;nbsp;sc.nextInt(); &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(base&amp;nbsp;&amp;lt;&amp;nbsp;2&amp;nbsp;||&amp;nbsp;base&amp;nbsp;&amp;gt;&amp;nbsp;62)&amp;nbsp;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;throw&amp;nbsp;new&amp;nbsp;IllegalArgumentException(&quot;Base&amp;nbsp;must&amp;nbsp;be&amp;nbsp;between&amp;nbsp;2&amp;nbsp;and&amp;nbsp;62.&quot;); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;long&amp;nbsp;result&amp;nbsp;=&amp;nbsp;0; &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;(int&amp;nbsp;i&amp;nbsp;=&amp;nbsp;0;&amp;nbsp;i&amp;nbsp;&amp;lt;&amp;nbsp;s.length();&amp;nbsp;i++)&amp;nbsp;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;char&amp;nbsp;c&amp;nbsp;=&amp;nbsp;s.charAt(i); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int&amp;nbsp;value; &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(c&amp;nbsp;&amp;gt;=&amp;nbsp;'0'&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;c&amp;nbsp;&amp;lt;=&amp;nbsp;'9')&amp;nbsp;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;value&amp;nbsp;=&amp;nbsp;c&amp;nbsp;-&amp;nbsp;'0'; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;else&amp;nbsp;if&amp;nbsp;(c&amp;nbsp;&amp;gt;=&amp;nbsp;'A'&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;c&amp;nbsp;&amp;lt;=&amp;nbsp;'Z')&amp;nbsp;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;value&amp;nbsp;=&amp;nbsp;c&amp;nbsp;-&amp;nbsp;'A'&amp;nbsp;+&amp;nbsp;10; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;else&amp;nbsp;if&amp;nbsp;(c&amp;nbsp;&amp;gt;=&amp;nbsp;'a'&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;c&amp;nbsp;&amp;lt;=&amp;nbsp;'z')&amp;nbsp;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;value&amp;nbsp;=&amp;nbsp;c&amp;nbsp;-&amp;nbsp;'a'&amp;nbsp;+&amp;nbsp;36; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;else&amp;nbsp;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;throw&amp;nbsp;new&amp;nbsp;IllegalArgumentException(&quot;Invalid&amp;nbsp;character:&amp;nbsp;&quot;&amp;nbsp;+&amp;nbsp;c); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(value&amp;nbsp;&amp;gt;=&amp;nbsp;base)&amp;nbsp;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;throw&amp;nbsp;new&amp;nbsp;IllegalArgumentException(&quot;Digit&amp;nbsp;&quot;&amp;nbsp;+&amp;nbsp;c&amp;nbsp;+&amp;nbsp;&quot;&amp;nbsp;is&amp;nbsp;not&amp;nbsp;valid&amp;nbsp;for&amp;nbsp;base&amp;nbsp;&quot;&amp;nbsp;+&amp;nbsp;base); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;result&amp;nbsp;=&amp;nbsp;result&amp;nbsp;*&amp;nbsp;base&amp;nbsp;+&amp;nbsp;value; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;System.out.println(result); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sc.close(); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} &lt;br /&gt;} &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;------------------------------------------------------------------------------------------------------------------&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;if&amp;nbsp;(c&amp;nbsp;&amp;gt;=&amp;nbsp;'0'&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;c&amp;nbsp;&amp;lt;=&amp;nbsp;'9')&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;value&amp;nbsp;=&amp;nbsp;c&amp;nbsp;-&amp;nbsp;'0';&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자가 '0'~'9'이면 '0'을 기준으로 아스키코드 차이를 계산하여&lt;br /&gt;해당 숫자를 실제 정수값(0~9)으로 변환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;else if (c &amp;gt;= 'A' &amp;amp;&amp;amp; c &amp;lt;= 'Z') {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;value = c - 'A' + 10;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;}&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자가 'A'~'Z'이면 'A'를 기준으로 몇 번째인지 계산하고,&lt;br /&gt;진법 규칙에 따라 10을 더해 실제 숫자값(10~35)으로 변환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;else if (c &amp;gt;= 'a' &amp;amp;&amp;amp; c &amp;lt;= 'z') {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;value = c - 'a' + 36;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;}&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;문자가 'a'~'z'이면 'a'를 기준으로 몇 번째인지 계산하고,&lt;br /&gt;Base62 규칙에 따라 36을 더해 실제 숫자값(36~61)으로 변환한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2)&amp;nbsp; 10진법 -&amp;gt; 다른 진법&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나눗셈을 반복하고 나머지를 거꾸로 읽는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예 29(10) -&amp;gt; 2진수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;29 &amp;divide; 2 = 14 ...1&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;14 &amp;divide; 2 = 7...0&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7 &amp;divide; 2 =&amp;nbsp; 3...1&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3 &amp;divide; 2 = 1...1&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1 &amp;divide; 2 = 0...1&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 11101이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) 10진법 -&amp;gt; 2진법(소수)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소수 부분은 곱셈법으로 구한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>하루 코딩</category>
      <author>tkdgud</author>
      <guid isPermaLink="true">https://tkdgud.tistory.com/11</guid>
      <comments>https://tkdgud.tistory.com/11#entry11comment</comments>
      <pubDate>Mon, 24 Nov 2025 16:30:51 +0900</pubDate>
    </item>
  </channel>
</rss>