<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>bora_u.log</title>
        <link>https://velog.io/</link>
        <description>인하대학교 컴퓨터공학과 학생입니다😀</description>
        <lastBuildDate>Wed, 01 Nov 2023 14:10:34 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>bora_u.log</title>
            <url>https://images.velog.io/images/bora_u/profile/cf54d856-01d7-4c14-8d2d-155b4fc4e512/보라.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. bora_u.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/bora_u" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[우테코] 2주차 과제 소감문]]></title>
            <link>https://velog.io/@bora_u/%EC%9A%B0%ED%85%8C%EC%BD%94-2%EC%A3%BC%EC%B0%A8-%EA%B3%BC%EC%A0%9C-%EC%86%8C%EA%B0%90%EB%AC%B8</link>
            <guid>https://velog.io/@bora_u/%EC%9A%B0%ED%85%8C%EC%BD%94-2%EC%A3%BC%EC%B0%A8-%EA%B3%BC%EC%A0%9C-%EC%86%8C%EA%B0%90%EB%AC%B8</guid>
            <pubDate>Wed, 01 Nov 2023 14:10:34 GMT</pubDate>
            <description><![CDATA[<p> 이번 과제는 1주차 과제와 비슷한 게임 구현 과제였지만, 추가적으로 테스트 코드를 짜야했다. 테스트 코드는 단위 테스트를 하는게 목적인데, 나는 테스트 코드 구현에 익숙하지 않아 어려움을 겪었다. 과제를 다 구현한 후 기능 별로 테스트를 하면 된다고 생각했는데, void 형식으로 구현한 함수와 private으로 구현한 후 public 함수 안에서 호출한 함수가 많아서 단위 테스트에 어려움을 겪었다. 그래서 이번에 구현한 프로그램에서는 세세한 단위테스트까지는 하지 못해서 다음 주차에는 테스트 코드에 대해 더 공부하여 단위 테스트에 용이하도록 설계해야겠다고 다짐했다. 어디에선가 테스트를 위한 코드를 짜지 말라는 말을 봤지만, 단위 테스트를 하기 쉽다는 것은 함수가 1개의 기능만을 하도록 잘 구현했다는 뜻이 아닐까? 초보 테스터로서 아직 잘 모르겠어서 2주차 피드백이 기대가 된다.</p>
<p> 프로그램 구현은 1주자처럼 MVC 패턴을 적용했는데 역할을 나눌 때 헷갈리는 부분이 있었다. View가 유저에게 화면을 표시해주는 역할을 해야하는데, 내가 구현한 코드에서는 각 자동차의 상황을 출력할 때 Game Model 안에서 출력을 하는게 훨씬 간단한 상황이었다. 오히려 View에서 출력을 하려고 하면 훨씬 더 복잡해지는 구조였다. 그래서 &quot;MVC 패턴을 적용하는게 항상 옳은가?&quot;라는 의문과, 앞서 말한 상황에서 &quot;필요할 경우에만 MVC 패턴을 지키지 않는 것은 괜찮은가?&quot;에 대해 생각해보게 되었다. 내가 내린 결론은 클래스들을 MVC 패턴에 따라 역할 별로 분리했기 때문에 번거롭더라도 View에서 출력을 해야한다는 것이었지만 아직 더 생각해보아야할 것 같다 :)</p>
<p> 또, 다른 사람들의 PR을 보고 자극을 많이 얻었던 것 같다. 내 과제를 제출한 후 여유가 생겨 다른 사람들의 PR과 코드 리뷰 내역을 보았는데, stream 함수 등 잘 몰랐던 정보들에 대해 알게 되었다. 만약 다음 과제에 String이나 배열을 사용하게 된다면 stream()을 사용해볼 계획이다.구현할 기능 목록도 잘 정리하신 분들이 많아서 나도 다음엔 더 잘 설계하고, 더 잘 정리해야겠다고 생각했다. </p>
<p> 그리고 프로그램을 구현하기 전에 구현할 기능 목록을 먼저 정리하여 커밋했는데, 실제로 구현을 하다보니 처음과 달라진 점이 많았던 것 같다. 하지만 오히려 처음에 작성한 것과 비교해보며 내가 어느 부분을 놓쳤는지 알 수 있어 좋았다.</p>
<p> 결론은 내가 그동안 프로그램 완성을 위해 반드시 필요한 단계인 테스트 단계를 간과했던 것 같다. 앞으로 테스트 코드를 작성하는 것을 습관들여야겠다고 느꼈다. 또, 테스트 코드 짜는 방법에 대해 공부하게 되어 뿌듯한 한 주였다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[우테코] 1주차 과제 소감문]]></title>
            <link>https://velog.io/@bora_u/%EC%9A%B0%ED%85%8C%EC%BD%94-1%EC%A3%BC%EC%B0%A8-%EA%B3%BC%EC%A0%9C-%EC%86%8C%EA%B0%90%EB%AC%B8</link>
            <guid>https://velog.io/@bora_u/%EC%9A%B0%ED%85%8C%EC%BD%94-1%EC%A3%BC%EC%B0%A8-%EA%B3%BC%EC%A0%9C-%EC%86%8C%EA%B0%90%EB%AC%B8</guid>
            <pubDate>Wed, 25 Oct 2023 12:51:18 GMT</pubDate>
            <description><![CDATA[<p> 이번 미션을 읽고 가장 먼저 해야할 일은 환경 설정이었는데, 평소에 깃&amp;깃허브 설정을 많이 해보아서 어려움 없이 성공할 수 있었다. 주변 지인 중 이번 프리 코스에 도전하는 사람이 꽤 있었는데, 환경 설정에 어려움을 겪는 사람들을 도와주기도 했다. 지인들이 프리코스를 통해 무엇을 얻어갈 수 있을까에 대해 많이 생각하는 듯했다. 첫 관문부터 어려움을 겪었어도 누군가가 대신 해주거나 알려주는 것만으로 해결하기 보다는 본인 스스로 성장하고 싶어해서, 약간의 도움을 주는 정도이긴 했다. 미션을 시작하기도 전에 주변 사람들을 보며 &#39;나도 성장할 수 있도록 노력해야지&#39;라는 다짐과 함께 동기부여를 얻을 수 있었다.</p>
<p> 비교적 간단한 프로그램이긴 하지만 바로 구현하기 보다는 기능 설계 단계에서 구현할 함수를 다 생각해놓은 후 구현했다. 미션에도 특히 기능 설계를 먼저 하라는 문구가 적혀 있었는데, 어느정도까지 자세히 해야할지 몰라 함수를 먼저 설계했다. 또, 최근에 소프트웨어 공학에 대해 공부하며 디자인 패턴을 접했고, 이번 미션에 디자인 패턴을 적용해서 도전해보겠다는 생각으로 시작했다. Spring Boot를 이용하여 RESTful API를 구현할 때 디자인 패턴을 이용하는 것은 익숙했지만, API가 아닌 프로그램을 디자인 패턴을 적용하여 구현해본 것은 처음이었다. JAVA를 쓰는 Spring Boot는 할 수 있는데 JAVA 단독으로는 잘 못한다니... 평소 Spring Boot를 공부하며 JAVA로 개발을 해왔으니 프리코스는 쉬울 것이라고 생각한 내 자신이 부끄럽게 느껴졌고 회의감이 들었다. 하지만 모르다면, 배우면 되는 것 아닌가?!! 그래서 먼저 MVC 패턴에 대해 공부했고 미션에 최대한 잘 적용하기 위해 노력했지만 솔직히 잘 적용이 됐는지는 모르겠다. 공통 피드백을 보고 코드를 리팩토링해 본 후 블로그에 회고록을 적어야겠다.</p>
<p> 미션을 구현하는 과정에서 Character와 char의 차이점, Integer과 int의 차이점, Character형 Array를 String으로 변환하는 방법 등 쉬운 것들을 기억하지 못해 검색을 하기도 했다. 또, 미션 중 사용자 입력을 받을 때 잘못된 입력이라면 IllegalAccessException 예외를 던지는 것이 있었다. 처음에는 그냥 main 함수에서 catch 해주었는데, 테스크 케이스 중 예외 테스트에서 계속 빌드에 실패했었다. 그래서 예외처리가 제 기능을하고, 이를 효과적으로 할 수 있도록 ExceptionHandler 클래스를 추가했는데 이걸 추가하는 과정에서 공부가 많이 된 것 같다!! </p>
<p> 1주차 과제를 해보고 느낀 점은 역시 기본이 정말 중요하다는 것이다!! 항상 기본 공부에 충실하도록 노력하고, 머리 속에 지식을 간직할 수 있는 사람이 되어야겠다...!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[AWS EC2] 공유기 바꾼 후 EC2에 SSH 접속 안됨 해결 방법, 트러블 슈팅]]></title>
            <link>https://velog.io/@bora_u/AWS-EC2-%EA%B3%B5%EC%9C%A0%EA%B8%B0-%EB%B0%94%EA%BE%BC-%ED%9B%84-EC2%EC%97%90-SSH-%EC%A0%91%EC%86%8D-%EC%95%88%EB%90%A8-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95-%ED%8A%B8%EB%9F%AC%EB%B8%94-%EC%8A%88%ED%8C%85</link>
            <guid>https://velog.io/@bora_u/AWS-EC2-%EA%B3%B5%EC%9C%A0%EA%B8%B0-%EB%B0%94%EA%BE%BC-%ED%9B%84-EC2%EC%97%90-SSH-%EC%A0%91%EC%86%8D-%EC%95%88%EB%90%A8-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95-%ED%8A%B8%EB%9F%AC%EB%B8%94-%EC%8A%88%ED%8C%85</guid>
            <pubDate>Sun, 29 Jan 2023 05:40:08 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요 보라입니다!</p>
<p>오늘은 공유기를 바꾼 후 EC2에 SSH 접속이 안 될 때 해결 방법에 대해 포스팅 해보겠습니다.</p>
<h3 id="문제-원인">문제 원인</h3>
<p>Spring boot 서버 배포를 하기 위해 평소와 같이 WinSCP를 이용하여 SSH 접근을 시도했습니다.</p>
<p><code>호스트를 찾는 중</code>이 계속 되다가 해당 오류가 발생했습니다. </p>
<p><code>네트워크 오류 : 연결 시간이 초과되었습니다</code></p>
<p>어제까지만 해도 잘 연결이 됐었는데 달라진게 있었다면 공유기를 변경했습니다.</p>
<p>아무래도 공유기 변경으로 인해 어떤(?) 설정이 달라진 것 같았습니다.</p>
<h3 id="해결-방안">해결 방안</h3>
<p>만약, 저처럼 인터넷 환경이 달라진게 아닌데 이런 오류가 뜬다면 해당 EC2 보안그룹 &gt; 인바운드 규칙에서 SSH 연결에 사용하는 22번 포트가 열려있지 않거나 현재 접근을 시도하는 IP가 허용 되지 않은 경우입니다.</p>
<p>이런 경우에는 다음과 같이 새로운 인바운드 규칙을 만들어서 Anywhere-IPv4를 선택하여 모든 IP의 접근을 허용하거나 내 IP만 허용해준 후 규칙을 추가해주면 됩니다. </p>
<p><img src="https://velog.velcdn.com/images/boraeyo/post/17d54e65-9d19-4906-b01a-8537b0248516/image.png" alt=""></p>
<p>저는 공유기를 바꿨기 때문에 내 IP가 달라지긴 했지만, 보안 그룹에서 Anywhere-IPv4를 선택하여 다 허용해준 상태였기 때문에 보안 그룹 문제는 아니었습니다.</p>
<h4 id="라우팅-테이블-확인">라우팅 테이블 확인</h4>
<p>계정이 생성되면 기본으로 설정되는 VPC가 있고, 관련해서 서브넷, 라우팅 테이블, 인터넷 게이트웨이 등이 생성된다고 합니다.</p>
<p>저는 라우팅 테이블의 <strong>인터넷 게이트 연결에 기존 IP 주소</strong>가 등록되어 있는 것이 문제였습니다. 라우팅 테이블은 VPC 내의 네트워크 트래픽을 전달하는 역할을 합니다.</p>
<p>AWS에서 <code>VPC 검색</code> &gt; <code>라우팅 테이블</code> &gt; <code>라우팅 테이블 ID 클릭</code> &gt; </p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/b4028897-c885-41dc-b8f2-eec2112197b9/image.png" alt=""></p>
<p>위 사진처럼 <code>0.0.0.0/</code> - <code>인터넷 게이트 웨이(파란 글씨)</code> 가 존재해야하고 정상적으로 활성화 돼있어야 합니다!</p>
<p>만약 없다면 인터넷 게이트웨이 선택 후 라우팅 테이블에 추가해주시면 됩니다.
<img src="https://velog.velcdn.com/images/boraeyo/post/fcb28476-7e2f-4d25-9092-31d49c90e85e/image.png" alt=""></p>
<p>이렇게 추가해주었더니 정상적으로 연결이 되었습니다!😊</p>
<h3 id="참고">참고</h3>
<p><a href="https://lemontia.tistory.com/812">https://lemontia.tistory.com/812</a> (정말 감사합니다!)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[UMC 3기] Spring boot와 AWS S3 연동하기, 퍼블릭 액세스 버킷 정책 생성하기]]></title>
            <link>https://velog.io/@bora_u/UMC-3%EA%B8%B0-Spring-boot%EC%99%80-AWS-S3-%EC%97%B0%EB%8F%99%ED%95%98%EA%B8%B0-%ED%8D%BC%EB%B8%94%EB%A6%AD-%EC%95%A1%EC%84%B8%EC%8A%A4-%EB%B2%84%ED%82%B7-%EC%A0%95%EC%B1%85-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@bora_u/UMC-3%EA%B8%B0-Spring-boot%EC%99%80-AWS-S3-%EC%97%B0%EB%8F%99%ED%95%98%EA%B8%B0-%ED%8D%BC%EB%B8%94%EB%A6%AD-%EC%95%A1%EC%84%B8%EC%8A%A4-%EB%B2%84%ED%82%B7-%EC%A0%95%EC%B1%85-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 25 Jan 2023 00:45:52 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요 보라입니다!</p>
<p>오늘은 Spring boot와 AWS S3를 연동하고 퍼블릭 액세스 허용을 위한 버킷 정책 생성 방법에 대해 포스팅해보려고 합니다.</p>
<h2 id="aws-s3란">AWS S3란?</h2>
<p>AWS S3는 온라인 객체 스토리지 서비스입니다. 쉽게 말하면 파일 저장 서비스이며 데이터를 객체 형태로 담아 놓는 바구니 역할을 합니다! 이미지나 동영상 파일들을 저장할 수 있으며 저장되는 모든 데이터를 객체라고 부릅니다.</p>
<p>온라인이라는 글자가 앞에 붙는 이유는 HTTP/HTTPS를 통한 API가 사용되기 때문이라고 합니다.</p>
<p>예를 들어, S3에 어떤 사진을 업로드 하면 그 사진은 객체로서 버킷에 저장됩니다. 각 객체는 단 하나의 유니크한 키를 가지며, 키를 통해서 객체를 식별할 수 있습니다. </p>
<h3 id="사용하는-이유">사용하는 이유</h3>
<ul>
<li>저장 용량이 무한대</li>
<li>파일 저장에 최적화</li>
<li>파일 저장에 있어 EC2를 사용할 때보다 저렴함</li>
<li>EC2처럼 Auto Scaling이나 Load Balancing을 신경쓰지 않아도 됨</li>
<li>정적 웹페이지는 S3를 이용하면 성능도 높아지고 비용 절감도 가능</li>
<li>HTTP 프로토콜로 파일 업로드/다운로드가 가능함</li>
</ul>
<h2 id="spring-boot와-s3-연동하기">Spring Boot와 S3 연동하기</h2>
<h3 id="s3-생성">S3 생성</h3>
<p>AWS에서 S3를 검색한 후 버킷 만들기를 눌러줍니다.
<img src="https://velog.velcdn.com/images/bora_u/post/bb1b8480-2488-4534-9779-3961b9a10488/image.png" alt=""></p>
<p>버킷 이름을 입력해줍니다. 리전은 서비스 지역에 따라서 선택해주면 되는데 저는 건드리지 않았습니다!
<img src="https://velog.velcdn.com/images/bora_u/post/27b091e8-abe2-49e6-9ba2-9d3a1a8e0f30/image.png" alt=""></p>
<p>사용자들이 사진을 업로드하거나 다운로드할 수 있어야하기 때문에 퍼블릭 액세스 차단을 풀어주었습니다. (그런데 보안 상 관점에서 이렇게 해도 되는 건지 헷갈려서 나중에 따로 찾아보려고 합니다...)
<img src="https://velog.velcdn.com/images/bora_u/post/5a15101b-9a17-4e9e-a615-7864e3bc1fd0/image.png" alt=""></p>
<p>버킷 버전 관리를 활성화 할 경우 파일을 버전 별로 관리합니다. 실수로 파일을 삭제하더라도 복원할 수 있지만 대신 비용이 더 발생할 수 있습니다.</p>
<p>기본 암호화를 활성화하면 버킷에 객체를 저장할 때 암호화해서 저장하고, 다운로드 할 때 복호화합니다. 저는 비활성화 해주었습니다.
<img src="https://velog.velcdn.com/images/bora_u/post/9ef74366-7d09-49fd-ab8a-962b912ad717/image.png" alt=""></p>
<p>그리고 버킷 만들기를 눌러줍니다.</p>
<h3 id="퍼블릭-액세스-설정">퍼블릭 액세스 설정</h3>
<p>버킷이 만들어졌지만 아직 퍼블릭 액세스는 불가능한 상태입니다. policy를 만들어 적용해주어야합니다. 방금 만든 버킷을 누르고 <code>권한</code> &gt; <code>버킷 정책</code> &gt; <code>정책 생성기</code>을 들어가줍니다.</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/09447b0b-903c-42d2-858e-e55198a7006c/image.png" alt=""></p>
<blockquote>
<ol>
<li>Select Type of Policy: S3 Bucket Policy</li>
<li>Effect(접근하는 사람을 선택할 것인가): Allow(모두 허용)</li>
<li>Principal(접근할 수 있는 사람): *(전체)</li>
<li>Actions: GetObject, PutObject, DeleteObject</li>
<li>Amazon Resource Name(ARN): arn:aws:s3:::todaysgym-bucket</li>
</ol>
</blockquote>
<p>Actions은 필요한 것만 설정해주면 됩니다. 저는 사진 업로드, 조회, 삭제를 위해 위 3가지만 설정해주었습니다.
ARN 부분에는 arn:aws:s3:::{내 버킷 이름} 이렇게 넣어주면 됩니다.</p>
<p>다 입력했다면 Add Statement를 눌러줍니다.</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/9277234e-47d4-44f9-b112-f651924b3835/image.png" alt=""></p>
<p>Generate Policy!</p>
<p>그러면 아래와 같이 창이 뜰 텐데, 이 내용들을 복사해줍니다.</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/4c32eb67-92de-4905-965a-8519ac438a5f/image.png" alt=""></p>
<p>버킷 정책에 붙여넣기 해주고 저장을 누릅니다.</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/8a2c8b29-7d53-4042-8346-1ca65884b054/image.png" alt=""></p>
<h4 id="알-수-없는-오류">알 수 없는 오류</h4>
<p>Action does not apply to any resource(s) in statement 라는 에러가 떴습니다.
<img src="https://velog.velcdn.com/images/bora_u/post/b290a776-1a65-4633-a907-e77efaf6c816/image.png" alt=""></p>
<p>접근할 수 있는 resource의 경로를 지정해주지 않아서 그렇습니다.</p>
<p>위에 복사했던 정책에서, 아래 부분 Resource의 맨 뒤에 <code>/*</code>를 달아줍니다.
(특정 폴더에만 접근 가능하게 하고 싶으면 <code>/폴더이름</code> 을 입력하면 됩니다.)
<img src="https://velog.velcdn.com/images/bora_u/post/dc408c4d-7bf2-4a8a-bbd7-14bd995fe8a9/image.png" alt=""></p>
<p>그러면 이렇게 퍼블릭으로 권한 설정이 됩니다!
<img src="https://velog.velcdn.com/images/bora_u/post/faf431a5-bac3-4aca-9778-8cff4754b27f/image.png" alt=""></p>
<p>참고: <a href="https://24hours-beginner.tistory.com/151">https://24hours-beginner.tistory.com/151</a></p>
<h3 id="의존성-추가">의존성 추가</h3>
<p><code>spring-cloud-starter-aws</code> 라이브러리를 build.gradle에 추가해주었습니다.</p>
<p>가끔 왜이렇게 오류가 나는지... 의존성을 추가해줘도 못 찾는 우리 인텔리제이..</p>
<p>아래 링크 참고하여 해결해주었습니다.</p>
<p>참고: <a href="https://www.lesstif.com/spring/gradle-intellij-113345573.html">https://www.lesstif.com/spring/gradle-intellij-113345573.html</a></p>
<h3 id="applicationyml">application.yml</h3>
<p>application.yml에 다음과 같이 추가해주었습니다.</p>
<p>application.yml 파일은 .gitignore에도 설정되어 있고, 깃허브에도 올리면 안됩니다!!</p>
<p>따라서 팀원들에게 따로 전달해주었습니다.</p>
<pre><code class="language-java">spring: 
  servlet:
    multipart:
      max-request-size: 30MB
      max-file-size: 30MB

cloud:
  aws:
    s3:
      bucket: todaysgym-bucket
    region:
      static: ap-northeast-2
    stack:
      auto: false</code></pre>
<p>이렇게 하면 Spring boot - AWS S3 연동과 설정은 끝났습니다!</p>
<p>다음 포스팅에서는 파일을 업로드하고 삭제하는 코드를 작성해보겠습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[UMC 3기] AWS EC2 Spring boot 프로젝트 오늘의 짐(GYM) 배포]]></title>
            <link>https://velog.io/@bora_u/UMC-3%EA%B8%B0-AWS-EC2-Spring-boot-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%B0%B0%ED%8F%AC</link>
            <guid>https://velog.io/@bora_u/UMC-3%EA%B8%B0-AWS-EC2-Spring-boot-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%B0%B0%ED%8F%AC</guid>
            <pubDate>Sun, 15 Jan 2023 23:00:18 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요 보라입니다!</p>
<p>오늘은 저번 포스팅에서 만들었던 AWS EC2에 Spring boot 프로젝트를 배포해보겠습니다.</p>
<p>아래 포스팅을 참고하여 진행했습니다!</p>
<p>참고: <a href="https://velog.io/@tritny6516/Spring-Boot-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0-Nginx-HTTPS-%EC%84%A4%EC%A0%95">https://velog.io/@tritny6516/Spring-Boot-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0-Nginx-HTTPS-%EC%84%A4%EC%A0%95</a></p>
<h3 id="ec2-서버에-jdk-설치">EC2 서버에 jdk 설치</h3>
<pre><code>$ sudo apt-get update 
$ sudo apt-get install openjdk-11-jdk // 자바 11 설치
$ java -version</code></pre><h3 id="spring-boot-프로젝트-build">Spring boot 프로젝트 Build</h3>
<p>먼저 배포하려는 Spring boot 프로젝트를 .jar 파일로 만들어야합니다.</p>
<p>인텔리제이 터미널에서 ./gradlew build 명령어를 입력하여 빌드 하면 자동으로 .jar 파일이 생성됩니다. </p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/7927bff7-c7cd-4c36-8679-e854f370eb62/image.png" alt=""></p>
<p>이때 application.yml 파일이 .gitignore에 올라가 있다면 잠시 해제하고 빌드해주시면 됩니다.</p>
<p>그렇지 않으면 DB 경로를 설정해주지 않아서 나타나는 에러인 &#39;Failed to configure a DataSource: &#39;url&#39; attribute is not specified and no embedded datasource could be configured.&#39; 에러가 뜰 수 있습니다!</p>
<p>빌드를 마쳤다면 이렇게 libs 디렉토리 안에 .jar 파일이 생길 것입니다.</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/22e94e44-beae-4207-9089-8eea15a41660/image.png" alt=""></p>
<h3 id="winscp에서-내-ec2에-jar-파일-넣기">WinSCP에서 내 EC2에 .jar 파일 넣기</h3>
<p>저는 저번 포스팅에서 WinSCP라는 프로그램을 이용하여 EC2 서버에 SSH 연결을 해놓은 상태입니다.</p>
<p>가상 환경 안에 server라는 이름의 디렉토리를 만들어주었습니다.</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/9bd0b0e7-f60e-45e6-bb30-2bc88bbc1ee7/image.png" alt=""></p>
<p>위 사진은 WinSCP 프로그램입니다. 왼쪽은 제 로컬 컴퓨터이고, 오른쪽은 ec2 서버의 가상 컴퓨터입니다.</p>
<p>제 로컬 컴퓨터를 나타내는 왼쪽에서 제가 배포하려는 프로젝트 폴더에 들어가줍니다. 그리고 방금 만든 .jar파일이 존재하는 libs에 들어갑니다.</p>
<p>이 파일을 오른쪽 server 디렉토리에 옮겨줄 것입니다.</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/c2eb2bb5-1659-4966-bba9-3e9ec9577525/image.png" alt=""></p>
<p>옮긴 후 터미널에서 확인해보니 잘 옮겨졌습니다.</p>
<p>그리고 우리가 인텔리제이에서 프로젝트를 실행하는 것처럼 다음과 같이 명령어를 입력하여 프로젝트를 실행해줍니다.</p>
<pre><code>$ java -jar 파일이름.jar
</code></pre><p><img src="https://velog.velcdn.com/images/bora_u/post/6e6b3b2f-c5df-4801-9698-098907491639/image.png" alt=""></p>
<p>잘 실행이 됩니다.</p>
<p>그리고 현재 포트 번호는 8123으로 되어있습니다. 퍼블릭IP:8123을 입력했더니 서버가 돌아가고 있습니다.
<img src="https://velog.velcdn.com/images/bora_u/post/78ef56f3-ab0d-40f5-bf9c-8d84328c80b6/image.png" alt=""></p>
<h3 id="nginx-reverse-proxy">Nginx Reverse Proxy</h3>
<p>저는 저번 포스팅에서 제 ec2 가상 컴퓨터에 Nginx를 설치해주었고 HTTPS 설정과 도메인을 구입하는 것까지 끝냈습니다. </p>
<p>Ngnix를 설치하지 않으셨다면 다음과 같이 해주시면 됩니다.</p>
<pre><code>$ sudo apt-get update
$ sudo apt install nginx -y
$ nginx -v // Nginx 버전 확인
$ sudo service nginx status // Nginx 상태 확인
$ sudo service nginx start // Nginx 시작</code></pre><p>이제 Nginx와 Spring boot 프로젝트를 연결해보겠습니다.</p>
<p>저번에 설정해준 것 때문에 여전히 https://퍼블릭:IP 또는 https://{도메인} 으로 접속했을 때 Nginx 서버가 돌아가고 있는데요.</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/9ea4d2ed-d3c0-47d1-9d35-46518c6206d0/image.png" alt="">
<img src="https://velog.velcdn.com/images/bora_u/post/b6412600-a6af-4dc7-be51-5eacb4104310/image.png" alt=""></p>
<p>proxy 설정을 해주겠습니다.</p>
<pre><code>$ sudo mkdir /var/log/nginx/proxy/    
$ sudo vi /etc/nginx/proxy_params</code></pre><p>포트번호가 80인 http에서 요청이 오면, Spring Boot 프로젝트에서 8123번 포트를 바라볼 수 있도록 proxy_params에 아래의 코드를 작성해줍니다.</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/3f7117db-ea3e-4286-ac0c-4e965f21a060/image.png" alt=""></p>
<h3 id="도메인-aws-route-53">도메인 AWS Route 53</h3>
<p>저는 이미 전 포스팅에서 도메인을 구입했기 때문에, 따로 설정만 해주었습니다.</p>
<p>AWS에서 Route 53 검색 &gt; 호스팅 영역 생성</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/00d84796-cecb-4549-95e9-d7b80e9760d1/image.png" alt=""></p>
<p>위와 같이 도메인이름 입력 후 호스팅 생성</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/3c5b6158-f016-4fcd-ac8f-a16eb50bc388/image.png" alt=""></p>
<p>그럼 이렇게 레코드 생성 버튼이 뜹니다. 눌러줍시다.</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/91ea81a6-dec8-479a-b48b-05c1e897c1c7/image.png" alt=""></p>
<p>값에 내 EC2의 퍼블릭 IP를 입력해주고 레코드 생성!</p>
<p>그럼 이렇게 레코드가 3개가 됩니다. 이 중 유형이 NS인 레코드를 보면 값/트래픽 라우팅 대상에 4개의 주소가 있을 겁니다.
<img src="https://velog.velcdn.com/images/bora_u/post/65e770aa-7d03-4353-8567-049da835fb3e/image.png" alt=""></p>
<p>가비아에 들어가보면 내 도메인 관리에서 다음과 같이 네임서버 설정칸이 있습니다. 여기에 위에서 언급한 4개의 주소를 채워주면 됩니다.</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/5d43bfca-cdfa-4311-a574-8631f50dc2b5/image.png" alt=""></p>
<p>우선 도메인 연결은 끝났습니다.</p>
<h3 id="서버-블록-생성">서버 블록 생성</h3>
<p>도메인을 이용하기 위해서 서버 블록을 생성해줘야합니다.</p>
<pre><code>$ sudo vi /etc/nginx/sites-available/{domain}

// 실제 입력한 명령어
$ sudo vi /etc/nginx/sites-available/todaysgym.shop</code></pre><p>명령어를 입력하여 새로운 파일을 만들어주고 아래와 같이 입력해주었습니다.
<img src="https://velog.velcdn.com/images/bora_u/post/c23b055d-6131-467a-a795-f90cc11b80b7/image.png" alt=""></p>
<p>이 코드는 제가 구입한 도메인 이름을 지정해주고, proxy_pass 설정을 통해 8123번 포트에 접속해야 볼 수 있는 화면(Spring Boot 프로젝트 화면)을 80번(HTTP) 포트에 접속했을 때 확인할 수 있도록 설정해줍니다. 즉 Reverse proxy의 기능을 하게 하는 코드입니다.</p>
<p>그리고 server_name을 추가할 때 해시 버킷 메모리 문제가 발생할 수 있다고 합니다. 아래와 같이 명령어를 입력하여 설정해줍니다.</p>
<pre><code>$ sudo vi /etc/nginx/nginx.conf</code></pre><p>해당 명령어를 입력했을 때 나오는 파일에서 아래 부분 주석 처리를 제거해줍니다.</p>
<pre><code>http { ...
    server_names hash_bucke_size 64;    # 주석 처리 제거
    ...
}</code></pre><h3 id="서버-블록-활성화">서버 블록 활성화</h3>
<p>아래 명령어를 입력하여 <code>sites-available</code> 디렉토리와 <code>sites-enabled</code> 디렉토리 간에 링크를 생성해 새로 만든 서버 블록 파일을 활성화 해줬습니다.</p>
<pre><code>$ sudo ln -s /etc/nginx/sites-available/{domain} /etc/nginx/sites-enabled/

//실제 입력한 명령어
$ sudo ln -s /etc/nginx/sites-available/todaysgym.shop /etc/nginx/sites-enabled/</code></pre><p>그리고 원래 기본적으로 <code>sites-available</code> 디렉토리와 <code>sites-enabled</code> 디렉토리에는 default 파일이 존재합니다. 이 파일이 존재하면 새로운 서버 블록 연결이 안되기 때문에 삭제해주겠습니다.</p>
<pre><code>$ sudo rm  /etc/nginx/sites-available/default
$ sudo rm  /etc/nginx/sites-enabled/default</code></pre><p>이제 정말 끝났습니다!!</p>
<h3 id="nginx-재시작">Nginx 재시작</h3>
<p>Nginx 서버를 재시작 해준 후에 Spring Boot 프로젝트를 실행해보겠습니다!</p>
<pre><code>$ sudo nginx -t // 구문 오류 테스트
$ sudo service nginx reload
$ java -jar {jar 파일명}.jar</code></pre><p>오류가 발생하지 않았다면 테스트를 해봅시다!</p>
<p>저는 Spring boot 프로젝트에 배포해놓은 api를 통해 테스트해보겠습니다.</p>
<p>이렇게 퍼블릭IP 주소만 입력하거나, <code>퍼블릭IP:{port#}</code>를 입력하면 다음과 같은 결과가 나옵니다.</p>
<p>이 api는 미리 연결해놓은 db의 Category라는 테이블의 카테고리 이름들을 모두 보여주는 api입니다. </p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/bc206494-1acc-4018-9eff-b79648cff91f/image.png" alt=""><img src="https://velog.velcdn.com/images/bora_u/post/eeeac0b7-1f34-4c82-8f3e-7ee7a0b25653/image.png" alt=""></p>
<p>자 이번에는... 도메인 주소로 요청해보겠습니다. <code>todaysgym.shop/categories</code> 를 입력해줍니다.</p>
<p>두구두구,,, 성공적이네요! </p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/3e859d13-99b1-4db2-a225-804eee4661f8/image.png" alt=""></p>
<h3 id="터미널을-종료해도-돌아가도록-배포">터미널을 종료해도 돌아가도록 배포!</h3>
<p>&#39;nohup&#39; 명령어와 &#39;&amp;&#39; 명령어를 이용하여 우분투 터미널의 세션 연결이 종료됐을 때도 해당 서버를 계속 실행할 수 있습니다.</p>
<p>&#39;nohup&#39;은 로그아웃으로 세션이 종료되더라도 프로그램이 종료되지 않도록 해주고, &#39;&amp;&#39;은 사용자 눈에 보이지 않도록 해주는 백그라운드 실행을 나타내는 명령어입니다.</p>
<p>두 명령어를 같이 사용하면 종료 없이 백그라운드에서 실행할 수 있습니다. 즉, 무중단 배포가 가능합니다.</p>
<pre><code>$ nohup java -jar {jar 파일명}.jar &amp;

// 중지시키고 싶을 때!!
$ ps -ef | grep java // 백그라운드 실행 프로세스 확인
$ kill -9 {중지시키려는 PID}</code></pre><h4 id="배포-꿀팁">배포 꿀팁</h4>
<p>아직 자동화 배포가 설정이 안 되어 있어서 수정사항이 생길 때마다 배포를 직접 해야했는데요, </p>
<p><code>$ nohup java -jar {jar 파일명}.jar &amp;</code> </p>
<p>실행 명령어를 딱 1번만 입력해 준 후에 잠시 기다려준 다음!! 테스트 해보면 잘 됩니다!  </p>
<p>백그라운드로 실행했기 때문에 프로젝트가 실행되는게 눈에 보이지 않기 때문에 (인텔리제이에서도 실행시키면 약 30초 정도 걸리는 것처럼..) 기다려줘야합니다.</p>
<p>바로 서버가 실행될 것이라고 생각하고(바보) 명령어를 한 번 치고 나서 바로 API 테스트를 했더니 테스트가 실패해서 계속 껐다 켰다를 반복했습니다 ㅋㅋㅋㅋ</p>
<p>심지어는 이미 백그라운드에서 실행해놓고, 왜 명령어 쳤는데 실행 안 되지? 하면서 포그라운드에서도 실행시켰네요 ㅋㅋㅋㅋ (덕분에 서버 과부하로 인스턴스 재부팅을 많이 했어요...)</p>
<h3 id="https-연결">HTTPS 연결</h3>
<p>음.. default 파일을 제거해주었더니 저번에 해놓았던 certbot https 연결이 해제되었습니다.
새로 연결할 방법을 찾고 따로 포스팅을 작성해야할 것 같습니다.</p>
<p>다음 포스팅 때 뵙겠습니다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[UMC 3기] 오늘의 짐(GYM) 개발환경 세팅 ]]></title>
            <link>https://velog.io/@bora_u/UMC-3%EA%B8%B0-%EC%98%A4%EB%8A%98%EC%9D%98-%EC%A7%90GYM-%EA%B0%9C%EB%B0%9C%ED%99%98%EA%B2%BD-%EC%84%B8%ED%8C%85</link>
            <guid>https://velog.io/@bora_u/UMC-3%EA%B8%B0-%EC%98%A4%EB%8A%98%EC%9D%98-%EC%A7%90GYM-%EA%B0%9C%EB%B0%9C%ED%99%98%EA%B2%BD-%EC%84%B8%ED%8C%85</guid>
            <pubDate>Tue, 10 Jan 2023 10:12:04 GMT</pubDate>
            <description><![CDATA[<h2 id="spring-initializr">Spring Initializr</h2>
<p>Spring 프로젝트를 만들 때 세팅해야할 것이 많은데요, Spring Initializr를 이용하여 쉽게 생성할 수 있습니다.</p>
<p>Spring Initializr 주소: <a href="https://start.spring.io">https://start.spring.io</a></p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/83cdd1c2-7046-464c-95d3-883356b8e0e0/image.png" alt=""></p>
<p>저는 위와 같이 구성하였습니다.</p>
<ul>
<li>Project: Gradle</li>
<li>Language: Java(11)</li>
<li>Spring Boot: 2.7.7</li>
<li>Dependencies: Spring Web, Spring Data JPA, Lombok, MySQL Driver</li>
</ul>
<p>구성이 끝났으면 GENERATE를 누르고, 생성된 파일을 인텔리제이에서 빌드해주었습니다! 끗!</p>
<h2 id="aws-ec2">AWS EC2</h2>
<p>클라우드 서버로 AWS EC2를 사용할 것입니다! 새로운 계정을 파주고 인스턴스 생성을 눌러줍니다.</p>
<h4 id="인스턴스-설정">인스턴스 설정</h4>
<ul>
<li><p>OS 이미지: Ubuntu 20.04
<img src="https://velog.velcdn.com/images/bora_u/post/6b85875d-fe50-42f2-8b93-ea9c24d78334/image.png" alt=""></p>
</li>
<li><p>인스턴스 유형: t2.micro</p>
</li>
<li><p>키페어 생성(.pem) → SSH 등록할 때 사용해야하므로 위치 기억해주기!
<img src="https://velog.velcdn.com/images/bora_u/post/641e8ec1-4a4d-400a-9344-f5bc099f0c14/image.png" alt=""></p>
</li>
<li><p>네트워크 설정: 보안 그룹 생성
<img src="https://velog.velcdn.com/images/bora_u/post/47ff548c-a385-4e78-83f8-444e2290a0bd/image.png" alt=""></p>
</li>
<li><p>스토리지 구성: 30 GiB
<img src="https://velog.velcdn.com/images/bora_u/post/09f818d7-da02-47e5-bb17-d46855776e5e/image.png" alt=""></p>
</li>
<li><p>요약
<img src="https://velog.velcdn.com/images/bora_u/post/b1353b51-373d-4123-9c8f-146985529a7d/image.png" alt=""></p>
</li>
<li><p>보안그룹 설정: 임시로 다 열어놈!
<img src="https://velog.velcdn.com/images/bora_u/post/aff2c17f-1015-4a0b-afe5-1063b1507e3d/image.png" alt=""></p>
</li>
<li><p>탄력적 IP 할당: 할당 받은 후 인스턴스를 연결
탄력적 IP를 할당하면, 인스턴스를 껐다 켜도 같은 public IP로 사용할 수 있습니다.
<img src="https://velog.velcdn.com/images/bora_u/post/5d4b5661-fec1-45fa-a17a-13c08f3a15f6/image.png" alt="">
위처럼 탄력적 IP를 할당 받고 아래처럼 인스턴스를 연결해줍니다다.
<img src="https://velog.velcdn.com/images/bora_u/post/4a9b0bf5-1719-4a08-8e78-62ac096f025e/image.png" alt=""></p>
</li>
<li><p>WinSCP를 이용해 서버 연결
<img src="https://velog.velcdn.com/images/bora_u/post/cf354d36-43a1-4c48-98b4-eb555f3d3e41/image.png" alt=""></p>
</li>
</ul>
<p>SSH 설정을 해주어야합니다.</p>
<p>고급 &gt; SSH &gt; 인증 &gt; 개인키 파일 &gt; 아까 다운받은 키페어 위치로 가서 선택
이때 안 보인다면 PuTTY 개인 키 파일에서 모든 개인 키 파일로 바꿔주면 됩니다.
그러면 PuTTY 형식으로 변환을 할거냐고 물어보는데 예를 눌러서 변환을 해줍니다~</p>
<p>이제 PuTTY에 접속이 되는지 확인해보았는데, 잘 되네요! </p>
<h4 id="서버-환경-설정">서버 환경 설정</h4>
<p>제가 예전에 작성했던 환경 설정을 따라서 진행해주었습니다.</p>
<p><a href="https://velog.io/@bora_u/UMC-3%EC%A3%BC%EC%B0%A8-%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95-%EC%8B%A4%EC%8A%B5">https://velog.io/@bora_u/UMC-3%EC%A3%BC%EC%B0%A8-%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95-%EC%8B%A4%EC%8A%B5</a></p>
<p>이것도 해주면 이제 RDS만 남았네요!</p>
<h2 id="rds">RDS</h2>
<p>rds는 전에 작성했던 포스팅을 따라 만들어주었습니다 ㅎㅎ</p>
<p><a href="https://velog.io/@bora_u/UMC-5%EC%A3%BC%EC%B0%A8-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%8B%A4%EC%8A%B5">https://velog.io/@bora_u/UMC-5%EC%A3%BC%EC%B0%A8-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%8B%A4%EC%8A%B5</a></p>
<p>끘!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[UMC 3기] 앱 런칭 프로젝트 PM!! 오늘의 짐(GYM)]]></title>
            <link>https://velog.io/@bora_u/UMC-3%EA%B8%B0-%EC%95%B1-%EB%9F%B0%EC%B9%AD-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-PM-%EC%98%A4%EB%8A%98%EC%9D%98-%EC%A7%90GYM</link>
            <guid>https://velog.io/@bora_u/UMC-3%EA%B8%B0-%EC%95%B1-%EB%9F%B0%EC%B9%AD-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-PM-%EC%98%A4%EB%8A%98%EC%9D%98-%EC%A7%90GYM</guid>
            <pubDate>Tue, 10 Jan 2023 09:13:33 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요🙌 
UMC 3기 서버 트랙이 2기와 내용이 같아서 블로그 정리를 하지 않았더니.. 앱 런칭 프로젝트를 할 때가 되어서야 드디어 글을 쓰게 되었습니다!</p>
<p>2기에는 앱 런칭 프로젝트에서 팀원으로 참가했었는데, 3기 때는 도전 정신을 발휘하여 제가 직접 앱을 기획했고 감사하게도 PM을 맡게 되었습니다😊</p>
<h2 id="오늘의-짐gym---아바타와-함께-성장하는-운동-기록--sns">오늘의 짐(GYM) - 아바타와 함께 성장하는 운동 기록 &amp; SNS</h2>
<h3 id="기획-배경">기획 배경</h3>
<p>저는 이 벨로그를 시작할 때 쯤에 주짓수라는 운동을 같이 시작했고, 아직까지도 꾸준히 운동을 하고 있습니다! 제가 운동을 하면서 느꼈던 불편한 점이 있었는데, 바로 그 날 배운 수업 내용을 저장할 공간이 부족했다는 겁니다...! 보통 휴대폰 메모장에 저장했었지만 양이 늘어나니 뒤죽박죽이었고, 날짜 별로 확인할 수도 없었습니다. 또, 평소 주짓수를 좋아하는 사람들을 도장밖에서도 만나고 이야기를 나누어 보고 싶었지만 그럴만한 공간이 마땅히 없었다는 점..? </p>
<p>이렇게 직접 겪은 불편함 + 필요성에 기반해서 앱을 기획했습니다!</p>
<ul>
<li>운동 기록을 하면 성장하는 아바타를 통해 운동 욕구 자극!</li>
<li>태그 기능으로 쉽게 운동 기록하기!</li>
<li>나와 같은 운동을 하는 사람들이 함께하는 깊이있는 소통의 공간</li>
<li>하루의 운동을 쉽게 기록하고, 아바타를 성장시키고, SNS에서 자랑하자!</li>
</ul>
<p><img src="https://velog.velcdn.com/images/bora_u/post/c76737c6-311f-4f82-819b-c88511b35789/image.png" alt=""></p>
<p>중요한 점은 주짓수 뿐만 아니라 헬스, 크로스핏, 클라이밍, 태권도, 무에타이, 검도 등... 다양한 운동에 대해 서비스를 지원한다는 점입니다 ㅎㅎ</p>
<h3 id="팀원-모집">팀원 모집</h3>
<p>저희 팀은 UMC BOLD 지부 내에서 모집을 받아 다음과 같이 이루어졌습니다!
그리고 저는 PM 겸 Back-End 파트를 맡았습니다.</p>
<ul>
<li>Front-End: 3명</li>
<li>Back-End: 4명</li>
<li>Designer: 1명</li>
</ul>
<h3 id="기술-스택-및-개발-환경">기술 스택 및 개발 환경</h3>
<ul>
<li>Front-End: Android</li>
<li>Back-End: Spring, Spring Boot, AWS, Docker</li>
<li>Database: Mysql</li>
<li>Design: Figma</li>
<li>Tool: Git, GitHub, Notion</li>
</ul>
<h3 id="화면-설계">화면 설계</h3>
<p>기획을 마친 후에는 화면 설계를 진행했습니다! 
어떤 화면이 필요하고, 그 화면이 어떤 기능을 가지고 있는지에 대해 정리했습니다. 제가 제작한 화면 설계를 바탕으로 디자이너분께서 GUI를 제작해주실 예정이었기 때문에 조금 오래 걸리더라도 꼼꼼하게 제작했습니다.</p>
<p>figma를 이용해 설계했는데, 처음 다뤄 보는 것이어서 역시 시간이 오래 걸렸습니다.. 그래도 결과물은 대만족✨ 자세히 말고 큰 화면으로 대강 이런 느낌으로 했다~ 정도만 봐주시면 감사하겠습니다 ㅎㅎ </p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/97bcb98e-e92e-4f66-b17d-37c55b810db3/image.png" alt=""></p>
<h3 id="첫-미팅">첫 미팅</h3>
<p>첫 미팅은 2023년 1월 6일 금요일에 강남역 근처 스터디 룸에서 진행했습니다!
팀원들 간 인사, 정기 회의 날짜 정하기, 세세한 기획(기획이 부족한 부분이 있었는데 팀원들의 의견을 물어 결정했습니다ㅎㅎ), GUI 소개 등을 진행했습니다.</p>
<h3 id="erd">ERD</h3>
<p>글을 작성하는 오늘, 서버 파트의 첫 회의가 있었고 ERD를 설계 했습니다.</p>
<p>각자 화면 설계서를 바탕으로 ERD를 설계해 온 후, 서로 비교해가며 부족한 부분을 채워가는 식으로 완성했습니다.</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/e8daedca-fe7d-4682-b8e0-0072e9c1a9e3/image.png" alt=""></p>
<h3 id="이제-개발-시작">이제 개발 시작!</h3>
<p>이번주에는 AWS EC2와 RDS를 파고 GitHub에 템플릿을 올려 팀원 모두 개발 환경을 셋팅한 후, 개발을 본격적으로 시작할 예정입니다! 다음 포스팅에서 뵙겠습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[UMC 3기] 아이디어톤&해커톤 수상 후기]]></title>
            <link>https://velog.io/@bora_u/UMC-3%EA%B8%B0-%EC%95%84%EC%9D%B4%EB%94%94%EC%96%B4%ED%86%A4%ED%95%B4%EC%BB%A4%ED%86%A4-%EC%88%98%EC%83%81-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@bora_u/UMC-3%EA%B8%B0-%EC%95%84%EC%9D%B4%EB%94%94%EC%96%B4%ED%86%A4%ED%95%B4%EC%BB%A4%ED%86%A4-%EC%88%98%EC%83%81-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Mon, 28 Nov 2022 05:09:53 GMT</pubDate>
            <description><![CDATA[<p>최근 UMC 3기 아이디어톤과 해커톤에 참가하였고 감사하게도 두 대회 모두 수상하였습니다! 그래서 UMC 3기 활동에 대한 첫 포스팅으로 아이디어톤&amp;해커톤 수상 후기를 적어보려합니다.</p>
<p>UMC에는 여러 지부가 있는데, 제가 재학 중인 인하대학교는 BOLD 지부에 속해있습니다. 이번 아이디어톤은 인하대학교 내에서, 해커톤은 BOLD 지부 내에서 진행되었습니다!</p>
<h2 id="아이디어톤">아이디어톤</h2>
<h4 id="진행-날짜-2022년-10월-1일2일">진행 날짜: 2022년 10월 1일~2일</h4>
<h4 id="주제-라이프-스타일">주제: 라이프 스타일</h4>
<p>아이디어톤은 온라인으로 진행되었고 메타버스 플랫폼을 이용하여 팀을 매칭한 후 하루 동안 회의를 거쳐 기획을 완성하였습니다. 팀 매칭 같은 경우, 챌린저마다 각자 아이디어를 가진 상태에서 본인의 아이디어를 어필하여 3~4인의 팀을 자유롭게 구성하는 방식이었습니다! 저는 제대로 기획해보고 싶은 아이디어가 있었기에 적극적으로 팀원을 구인하였고, 감사하게도 제 아이디어로 팀 매칭에 성공하였습니다 XD</p>
<p>제가 기획한 서비스는 운동 기록 &amp; SNS 서비스입니다. 이미 스토어에 존재하는 운동 기록 서비스는 많지만, 저는 다른 가치를 추가하였습니다. 바로 비주류 운동까지 지원하는 서비스입니다! 저는 주짓수를 하고 있기에 평소에 제게 필요하다고 생각했던 서비스를 자세히 기획하게 되었습니다. </p>
<p>아래는 기획 내용입니다! </p>
<p><a href="https://out-of-window-bora.notion.site/GYM-SNS-9a36700d90f8471d8a2619244b4eca3c">https://out-of-window-bora.notion.site/GYM-SNS-9a36700d90f8471d8a2619244b4eca3c</a></p>
<h4 id="결과는-아이디어톤-본선-진출">결과는...! 아이디어톤 본선 진출!</h4>
<p>인하대학교 UMC 3기 아이디어톤은 각 주제 별 1,2위가 지부 아이디어톤에 진출하게 되는 구조였습니다. 저는 라이프 스타일2 주제 내에서 2위 안에 들어(정확한 순위는 알 수 없습니다) 본선에 진출하게 되었습니다~!</p>
<p>이번 아이디어톤에서 본선에 진출하게 되어, UMC의 꽃인 3기 앱 프로젝트에 팀장으로 지원하였습니다. 감사하게도 팀장으로 선정되어 이 서비스를 더 자세히 기획하고 개발할 수 있게 되었습니다. 이 서비스에 대한 내용은 앞으로 앱 런칭 프로젝트를 진행하며 자세히 올릴 예정입니다. 기대해주세요!</p>
<h2 id="해커톤">해커톤</h2>
<h4 id="진행-날짜-2022년-11월-12일13일">진행 날짜: 2022년 11월 12일~13일</h4>
<h4 id="주제-우리가-우리답게-살-수-있도록-도움을-주는-서비스">주제: 우리가 우리답게 살 수 있도록 도움을 주는 서비스</h4>
<p><strong>#대학생 #뭐하고사니? #PLAY #잘노는 #LifeStyle #운동 #Challenge #도전하는</strong></p>
<p>해커톤은 오프라인으로 진행되었는데 챌린저가 많다보니 참여할 수 있는 인원도 한정되어 있었습니다. 그래서 해커톤 티켓팅(?)을 해야했습니다..! 저는 인하대학교 수강신청으로 단련되어있었기에... 1등으로 성공하였습니다💥</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/6ec9d26c-e149-4f22-ab87-146d362a8a48/image.jpg" alt=""></p>
<p>저는 백엔드 파트(Spring)로 참가하였고, 팀은 랜덤으로 구성되어 총 10명이 한 팀이었는데, Client 4명 Planner 1명 Designer 1명 Server 4명으로 이루어져있었습니다. 10명이서 주제에 맞게 아이디어에 대해 회의를 진행하였고 기획한 서비스를 각 파트 별로 개발하였습니다. </p>
<p>그런데 이번 대회 목적은 <strong>같은 파트끼리의 협업</strong>이었기에 수상은 각 파트 별로 이루어졌습니다. 제가 참가한 백엔드 파트는 API를 분배하여 개발하기, 깃허브에 PR을 날리고 merge하는 것이 주 할 일 이었습니다!</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/3f45f0a2-f70d-4c8a-825d-80c13ea81717/image.jpg" alt=""></p>
<p>팀 마다 Git Repository가 주어졌고 API를 개발 할 때 협업 규칙을 지키며 commit 해야했습니다. </p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/89fa5f67-9d56-4501-86fd-e5313b0b022e/image.png" alt=""></p>
<p>연결하는 과정에서 Git 인증 정보 만료가 떠서 조금 고생했는데, 다음 블로그를 참고하여 해결하였습니다.</p>
<p><a href="https://velog.io/@kma7574/Sourcetree-%EC%9D%B8%EC%A6%9D-%EC%98%A4%EB%A5%98-%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95">https://velog.io/@kma7574/Sourcetree-%EC%9D%B8%EC%A6%9D-%EC%98%A4%EB%A5%98-%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95</a></p>
<h4 id="하루젝트---프로젝트-팀원-매칭-서비스">하루젝트 - 프로젝트 팀원 매칭 서비스</h4>
<p>ERD는 다음과 같이 설계하였습니다. 이때 처음 참가해보는 해커톤이여서 그런지.. 지금 생각해보면, 빠른 시간 안에 아이디어를 기획하고 개발해야하는 해커톤에는 적합하지 않게 ERD를 다소 복잡하게 설계한 것습니다.</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/6d68ddec-3951-4a21-9754-d0ed222e848c/image.png" alt=""></p>
<p>제가 맡은 API는 프로젝트 생성 &amp; 게시글 생성 API이었고 개발 시간은 그렇게 오래 걸리지 않았던 것 같습니다! 아래는 제가 발표했던 자료에서 가져왔습니다 ㅎㅎ</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/cadc79a2-7f88-4023-90fd-129b869d7d60/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/ef27b518-3353-4172-b217-718f505f15d0/image.png" alt=""></p>
<p>제가 발표 자료 만들고 발표까지 수행하였습니다. (저희 팀원분들 mbti가 다 I여서 다들 발표하기 싫어하시길래 각자 개발한 부분을 각자 발표하기로 했는데... 다른 팀 발표를 듣다 보니 제가 잘 발표할 수 있을 것 같아서 저 혼자 발표하겠다고 했습니다...! 다행히 발표를 잘한 것 같습니다 ㅋㅋㅋ)</p>
<h4 id="결과는">결과는...!</h4>
<p><img src="https://velog.velcdn.com/images/bora_u/post/501be77c-ecae-45a6-bb19-7dd4724f5b41/image.jpg" alt=""></p>
<p>각 팀의 점수는 챌린저끼리의 상호평가로 매겨졌습니다. 감사하게도 최우수상(2위)를 수상하였습니다~</p>
<h2 id="후기">후기</h2>
<p>아이디어톤은 UMC 2기 때도 해봤기 때문에, 처음이 아니었지만 수상은 처음이어서 기분이 좋았습니다 ㅎㅎ 해커톤은 처음 나가보는 것이었습니다! 평소에 무박으로 진행되는 해커톤이 멋져보여서(?) 저도 해보고 싶었는데 좋은 경험이 된 것 같습니다😋 특히 최근 2년 간 코로나로 인하여 오프라인 행사가 거의 진행되지 않았던 것으로 아는데, 오프라인 해커톤을 열어주신 UMC에게 정말 감사했습니다. 다음에도 해커톤에 나갈 기회가 있다면.. 이번 해커톤을 바탕으로 더!! 잘할 수 있을 것 같습니다😊</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[빵빵] Swagger 적용하기]]></title>
            <link>https://velog.io/@bora_u/Swagger-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@bora_u/Swagger-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Mon, 19 Sep 2022 10:06:12 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요! 오늘의 포스팅 주제는 Swagger입니다!</p>
<p>Spring boot를 이용하여 REST API를 개발하는 사람이라면, API 명세서를 작성해본 경험이 있을 것입니다. API 명세서는 해당 API에 대한 정보를 정리한 문서로, API를 사용하게 될 클라이언트와 다른 서버 개발자에게 공유하는 것이 그 목적입니다!</p>
<h2 id="swagger란">Swagger란?</h2>
<p>Swagger는 Open Api Sepcification(OAS)를 위한 프레임워크로, API 문서 자동화와 테스트 기능을 제공해줍니다. Swagger를 이용하면 변경사항이 있을 때마다 API 명세서를 직접 작성하거나 API를 이해하기 위해 소스코드를 살펴 볼 필요가 없게 됩니다. 약속된 규칙에 따라 json, yml 형식으로 표현해주면 html 페이지로 문서화해주고, 이를 통해 API를 이해할 수 있게 됩니다.</p>
<h2 id="swagger-실습">Swagger 실습</h2>
<h3 id="swagger-환경-설정">Swagger 환경 설정</h3>
<h4 id="개발환경">개발환경</h4>
<blockquote>
<p>Spring Boot 2.7.1
Swagger 3.0.0
Java 11</p>
</blockquote>
<h4 id="springfox-의존성-추가하기">springfox 의존성 추가하기</h4>
<p>build.gradle &gt; dependencies 우클릭 &gt; generate &gt; &#39;springfox&#39; 검색
<img src="https://velog.velcdn.com/images/bora_u/post/179f8679-da9a-4f09-ad8e-a33f5c0cf440/image.png" alt=""></p>
<p>아래 3개를 Add해줍니다! springfox-boot-starter 만 add 해도 된다고 알고 있는데, 우선 전 다 해줬습니다😀
<img src="https://velog.velcdn.com/images/bora_u/post/c9821743-00d7-4174-8902-35dfd3b6cc63/image.png" alt=""></p>
<p>그리고 swagger가 spring boot 2.6 이후 버전에서는 path가 달라 오류가 발생한다고 해서 application.yml에 다음 코드를 추가해주었습니다. 이렇게 지정해주면 오류가 발생하지 않는다고 하네요!</p>
<pre><code class="language-java">spring:
   mvc:
     pathmatch:
        matching-strategy:ant_path_matcher</code></pre>
<h3 id="swaggerconfig-설정">SwaggerConfig 설정</h3>
<pre><code class="language-java">@Configuration
@EnableSwagger2
public class SwaggerConfig {
    private static final String API_NAME = &quot;Bbang0 API&quot;;
    private static final String API_VERSION = &quot;0.0.1&quot;;
    private static final String API_DESCRIPTION = &quot;Bbang0 API 명세서입니다.&quot;;

    @Bean
    public Docket api() {
        Parameter parameterBuilder = (Parameter) new ParameterBuilder()
                .name(HttpHeaders.AUTHORIZATION)
                .description(&quot;Access Tocken&quot;)
                .modelRef(new ModelRef(&quot;string&quot;))
                .parameterType(&quot;header&quot;)
                .required(false)
                .build();

        List&lt;Parameter&gt; globalParameters = new ArrayList&lt;&gt;();
        globalParameters.add(parameterBuilder);

        return new Docket(DocumentationType.SWAGGER_2)
                .globalOperationParameters(globalParameters)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage(&quot;com.example.bbang0.adapter.controller&quot;))
                .paths(PathSelectors.any())
                .build();
    }

    public ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title(API_NAME)
                .version(API_VERSION)
                .description(API_DESCRIPTION)
                .build();
    }

}</code></pre>
<p><strong>ApiInfo</strong>
-&gt; API의 이름이 무엇이고 현재 버전은 어떻게 되는지 정보를 적어주면 됩니다.</p>
<p><strong>ParameterBuilder</strong>
-&gt; API를 테스트 할 때 모든 API에 전역 파라미터를 설정해줍니다.</p>
<p><strong>RequestHandlerSelectors.basePackage(String packageName)</strong>
-&gt; 스웨거를 적용해줄 클래스의 package 명을 적어줍니다.</p>
<p><strong>PathSelectors.any()</strong>
-&gt; 해당 package 하위에 있는 모든 url에 적용해줍니다.</p>
<p>출처 : <a href="https://kim-jong-hyun.tistory.com/49">https://kim-jong-hyun.tistory.com/49</a></p>
<h3 id="swagger-화면">Swagger 화면</h3>
<p>주소창에 다음 링크를 입력해봅시다.</p>
<blockquote>
<p>localhost:{port#}/swagger-ui/
localhost:{port#}/swagger-ui/index.html</p>
</blockquote>
<h4 id="트러블-슈팅--404-error">트러블 슈팅 : 404 error</h4>
<p>처음에는 주소창에 localhost:{port#}/swagger-ui.html 으로 검색하였고, 다음과 같은 404 에러가 떴습니다.</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/0ec919e1-78d6-4103-b2bb-e1e7f7e3916e/image.png" alt=""></p>
<p>이유는 springfox 3.0 이후 버전부터는 접속 링크가 다르기 때문이었습니다!
버전을 확인하셔서 올바른 링크로 접속하시면 될 것 같습니다.</p>
<p>자, 접속해보니 이런 화면이 뜹니다!</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/3fb42615-b29a-49b0-973c-1ec9e7e93366/image.png" alt=""></p>
<p>제가 적은 API_NAME, API_VERSION 등이 반영되어있습니다!</p>
<p>그러나 아직 각 API에 대한 설명이 부족하기 때문에, Controller에서 설명을 추가해줍시다.</p>
<h3 id="controller-코드-수정">Controller 코드 수정</h3>
<p><strong>@Api(tags = { &quot;breads&quot;})</strong></p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/aaca2ec5-0e7b-4864-be25-475dada9f46b/image.png" alt=""></p>
<p>Controller를 대표하는 최상단 타이틀 영역에 표시될 값을 적어줍니다.</p>
<p><strong>@ApiOperation(value = &quot;제목&quot;, notes = &quot;설명&quot;)</strong></p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/061d64f5-e41b-46a8-8e44-c8463a330855/image.png" alt=""></p>
<p>각각의 함수 위에 제목과 설명을 표시해줍니다.</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/2fb4afee-a19b-423c-9b14-f0763e922731/image.png" alt=""></p>
<p>API를 알아볼 수 있도록 제목과 설명을 더해주었더니 위와 같이 바뀌었습니다 XD</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/b082f2d3-f956-4b7a-817f-eedbc9f4cb53/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/99673468-095c-48b2-82dd-827390a63202/image.png" alt=""></p>
<p>이렇게 Example Value와 Response까지 알려줄 뿐더러, Try it out 버튼을 통해 테스트도 해볼 수 있습니다.</p>
<p>이제는 이런 방식으로 클라이언트 또는 다른 서버 개발자에게 간편하게 API를 전달하고 테스트할 수 있게 되었습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리눅스] 파일 다루기 실습]]></title>
            <link>https://velog.io/@bora_u/%EB%A6%AC%EB%88%85%EC%8A%A4-3%EC%A3%BC%EC%B0%A8-%EC%8B%A4%EC%8A%B5</link>
            <guid>https://velog.io/@bora_u/%EB%A6%AC%EB%88%85%EC%8A%A4-3%EC%A3%BC%EC%B0%A8-%EC%8B%A4%EC%8A%B5</guid>
            <pubDate>Thu, 15 Sep 2022 07:24:43 GMT</pubDate>
            <description><![CDATA[<h3 id="open">open()</h3>
<p>open 함수를 이용하여 단순히 파일을 여는 코드를 작성해보자</p>
<pre><code>$ vim ex_open.c
//파일 작성 후에,
$ gcc ex_open.c -o ex_open
$ ./ex_open</code></pre><p>ex_open.c 내용
<img src="https://velog.velcdn.com/images/bora_u/post/9a35aef4-f31d-4ab0-8795-c1c4b6f1a1d5/image.png" alt=""></p>
<p>testfile 파일을 처음 만든 경우,
<img src="https://velog.velcdn.com/images/bora_u/post/6d85bb2f-6ae5-4e6c-a810-f3b4a3746290/image.png" alt="">
이미 있는 파일을 열었을 경우,
<img src="https://velog.velcdn.com/images/bora_u/post/ec20af04-cafc-4494-a616-95b7568bd980/image.png" alt=""></p>
<h3 id="read--write">read() &amp; write()</h3>
<p>ex_cp.c 내용
<img src="https://velog.velcdn.com/images/bora_u/post/cdcf161d-dd20-4271-b04b-7d28dc995442/image.png" alt=""></p>
<pre><code>$ vim ex_cp.c

// 내용 작성 후 컴파일
$ gcc ex_cp.c -o ex_cp

// ex_copy.c에는 아무 내용이나 입력해준다.
$ vim ex_copy.c

// ./ex_cp [복사할 파일 이름] [복사될 파일 이름]
$ ./ex_cp ex_copy.c ex_copy.c.copy</code></pre><p>썽공 
<img src="https://velog.velcdn.com/images/bora_u/post/b6b8329b-2589-486c-ab5f-bf8177249f44/image.png" alt=""></p>
<h4 id="outfile이-이미-존재할-경우-아래와-같이-동작하도록-수정해보자">outfile이 이미 존재할 경우 아래와 같이 동작하도록 수정해보자</h4>
<p>outfile을 여는 부분을 다음과 같이 수정
<img src="https://velog.velcdn.com/images/bora_u/post/da7c7e2d-fdf5-4294-9b0f-b62ca97075d2/image.png" alt=""></p>
<p>결과는
<img src="https://velog.velcdn.com/images/bora_u/post/759549e6-1d3e-43c6-acb0-e3cbec6eea4b/image.png" alt=""></p>
<h3 id="dup와-dup2">dup()와 dup2()</h3>
<p>ex_dup.c 내용
<img src="https://velog.velcdn.com/images/bora_u/post/678f3fa1-cc80-4005-9dce-493617cdf348/image.png" alt=""></p>
<pre><code>$ vim ex_dup.c

//ex_dup.c 내용 작성 후
$ gcc ex_dup.c -o ex_dup
$ ./ex_dup testfile</code></pre><p>결과는!
<img src="https://velog.velcdn.com/images/bora_u/post/0bc013cf-6d3b-4ed5-ab1a-7ded5d75a1bf/image.png" alt=""></p>
<p>testfile 내용도 확인해보면
<img src="https://velog.velcdn.com/images/bora_u/post/ba041941-c571-4fcd-b566-0d9d41f7bcda/image.png" alt=""></p>
<h3 id="lseek">lseek()</h3>
<pre><code>#include &lt;unistd.h&gt;

off_t lseek(int filedes, off_t offset, int start_flag);</code></pre><p>filedes : lseek()을 수행할 파일</p>
<p>offset : 새롭게 지정할 포인터의 위치(음수가 될 수도 있음)</p>
<p>start_flag: offset의 기준</p>
<ul>
<li>SEEK_SET: 파일의 시작 위치를 기준</li>
<li>SEEK_CUR: 파일의 현재 작업 위치를 기준</li>
<li>SEEK_END: 파일의 끝을 기준</li>
</ul>
<h3 id="makefile-작성하기">Makefile 작성하기</h3>
<p>매크로를 사용하면 더 편리하게 작성할 수 있음</p>
<ul>
<li>매크로 사용
  • 정의: VAR=arguments
  • 사용: $(VAR)</li>
<li>$@: 출력 파일(target)을 지칭하는 매크로</li>
<li>$&lt;: 입력 파일(dependecies)을 지칭하는 매크로</li>
</ul>
<p><img src="https://velog.velcdn.com/images/bora_u/post/cb6b78e9-caa5-48c3-99c3-4598ee920c5a/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리눅스] 파일 기초 vim, 컴파일 ]]></title>
            <link>https://velog.io/@bora_u/%EB%A6%AC%EB%88%85%EC%8A%A4-2%EC%A3%BC%EC%B0%A8-%EC%8B%A4%EC%8A%B5</link>
            <guid>https://velog.io/@bora_u/%EB%A6%AC%EB%88%85%EC%8A%A4-2%EC%A3%BC%EC%B0%A8-%EC%8B%A4%EC%8A%B5</guid>
            <pubDate>Thu, 08 Sep 2022 07:19:04 GMT</pubDate>
            <description><![CDATA[<h2 id="파일-다루기---vim">파일 다루기 - vim</h2>
<pre><code>$ vim [파일 이름]
//해당 이름의 파일이 없는 경우 새로 생긴다.</code></pre><p>v : 비주얼 모드
y : 복사
p : 붙여넣기
검색하는 법 :  일반모드에서 /word 그리고 n을 누르면 다음꺼 :검색</p>
<h2 id="컴파일-하기">컴파일 하기</h2>
<pre><code>$ gcc [컴파일 할 파일.c] -o [컴파일 후 이름.o]
$ gcc hello.c -o hello.out</code></pre><p>이때 -o hello.out 옵션을 따로 처리해주지 않으면, a.out으로 컴파일 된다.</p>
<ul>
<li>-o : 컴파일 한 파일의 이름 설정</li>
<li>-c : 링킹 전까지만 컴파일</li>
</ul>
<pre><code>// -c 옵션을 통해 링킹은 하지 않음
$ gcc –c –o main.o main.c
$ gcc –c –o func_one.o func_one.c
$ gcc –c –o func_two.o func_two.c

// 여기서 링킹
$ gcc –o print_func main.o func_one.o func_two.o</code></pre><p>컴파일과 링킹이 잘 되었는 지 테스트해보면,</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/b9c4531c-64af-4c27-b457-212a5823d154/image.png" alt=""></p>
<p>잘됐음ㅋ</p>
<h2 id="실행하기">실행하기</h2>
<pre><code>$ ./hello.out</code></pre><h2 id="makefile">Makefile</h2>
<p>컴파일 시 필요한 규칙을 명시한 스크립트 파일</p>
<h4 id="기본작성-방법">기본작성 방법</h4>
<pre><code>&lt;target&gt; : &lt;dependency&gt;
        &lt;recipe&gt;
clean:
        &lt;remove command&gt;</code></pre><p>• target: 빌드 이름 대상. 이 rule이 만들어내는 최종 파일명
• dependency: 빌드 대상이 의존하는 파일 목록
• recipe: 빌드 대상을 생성하는 명령
• clean: make clean 명령어를 통해 수행할 작업 작성 (build 부산물 정리)
• “<dependency>에 있는 재료들로 <recipe> 명령어를 가지고 <target>을 만들어줘”</p>
<h4 id="makefile-예시">Makefile 예시</h4>
<p>all : print_func</p>
<p>print_func : main.o func_one.o func_two.o
        gcc -o print_func main.o func_one.o func_two.o</p>
<p>main.o : header.h main.c
        gcc -c main.c</p>
<p>func_one.o : header.h func_one.c
        gcc -c func_one.c</p>
<p>func_two.o : header.h func_two.c
        gcc -c func_two.c</p>
<p>clean :
        rm -f *.o print_func</p>
<p>헤더가 바뀌었는 지 확인 후 컴파일하기 위해서 각각 함수의 컴파일 대상에 header.h도 추가해준다.</p>
<p>그리고 make를 하면</p>
<p>  <img src="https://velog.velcdn.com/images/bora_u/post/6644e321-597c-436a-9387-07da82f3cd9b/image.png" alt=""></p>
<p>  이런식으로 성공~ (위 사진은 짤렸습니당 ㅎ)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[빵빵] DB 설계]]></title>
            <link>https://velog.io/@bora_u/%EB%B9%B5%EB%B9%B5-DB-%EC%84%A4%EA%B3%84</link>
            <guid>https://velog.io/@bora_u/%EB%B9%B5%EB%B9%B5-DB-%EC%84%A4%EA%B3%84</guid>
            <pubDate>Mon, 11 Jul 2022 09:45:14 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요! 보라입니다💜</p>
<p>UMC 10주차 트랙 과정에 이어서 프로젝트를 진행하게 되었는데요, 프로젝트 명은 &#39;빵빵&#39;입니다!</p>
<p>또, 감사하게도 소모임에서 진행하는 &#39;썸머코딩&#39; 백엔드(Spring) 멘토링을 받게 되었습니다! (선배님들 바쁜 시간 내주셔서 감사합니다😁) </p>
<p>그래서 이번 여름 방학 동안은 프로젝트 빵빵과 썸머코딩 진행 내용들이 올라올 예정입니다!</p>
<h2 id="1-프로젝트-빵빵-소개">1. 프로젝트 빵빵 소개</h2>
<ul>
<li><strong>프로젝트명</strong> : 빵(🍞)빵(0) - 세상에 더 이상 버리는 빵은 없다.</li>
<li><strong>프론트엔드</strong> : IOS</li>
<li><strong>백엔드</strong> : Spring Boot</li>
<li><strong>데이터 베이스</strong> : MySql</li>
<li><strong>문제점 정의</strong>
  짧은 단위로 만들고 판매하는 빵 같은 경우 유통기한이 길지 않기 때문에 모두 판매하지 못하고 폐기하는 빵들이 생기게 됩니다. 이렇게 빵을 폐기하는 경우 결국 음식물 쓰레기로써 처리해야 된다는 점과 빵 제작 비용 상실이라는 두 가지 문제점이 존재합니다.</li>
<li><strong>핵심기능</strong><ol>
<li>인기 빵집, 동네 빵집 두 가지 메뉴에 대해 파악</li>
<li>빵집에서 폐기 전인 세일하는 빵 종류와 수량 파악 후 플랫폼에 전달 </li>
<li>플랫폼에서 소비자들에게 해당 내용 공지 ( 푸시, 메시지 형식 )</li>
</ol>
</li>
<li><strong>Flow 차트</strong>
<img src="https://velog.velcdn.com/images/bora_u/post/5e3b8e7f-85de-4688-b4a6-a96500c9305b/image.png" alt=""></li>
<li><strong>와이어 프레임</strong>
<img src="https://velog.velcdn.com/images/bora_u/post/494a2b08-3d6a-4263-af5c-1711697656c7/image.png" alt=""></li>
</ul>
<p>그리고 저는 이번 프로젝트에서 PM님과 함께 백엔드를 맡게 되었습니다!!
(위 프로젝트 설명은 PM님께서 작성해주신 것을 가져왔습니다!)</p>
<h2 id="2-db-설계---1회차">2. DB 설계 - 1회차</h2>
<h3 id="1-table-구성-생각해보기">1) Table 구성 생각해보기</h3>
<p>데이터 베이스 설계를 위해, 먼저 어플의 와이어 프레임을 살펴보면서 어떤 Table 안에 어떤 column들이 필요할 지 생각해보았습니다.</p>
<ul>
<li><p>** User**
userIdx
userName
userPhone
userEmail
(starShop - 즐겨찾기 한 shop!)
createdAt
updatedAt
isShop - shop 권한이 있는 계정은 1
shopIdx - shop</p>
</li>
<li><p><strong>Shop</strong>
shopIdx
shopName
shopImgUrl - 빵집 대표 이미지
isOpen - shop이 열었으면 true
createdAt
updatedAt
shopLocation - 이 부분 잘 모르겠음..</p>
</li>
<li><blockquote>
<p>만약 shopIdx로 breadIdx를 검색했을 때 개수가 0 이면 타임 세일 중 x</p>
</blockquote>
</li>
<li><p><strong>Bread</strong>
breadIdx
shopIdx - Shop 테이블에서 참조
breadName
breadCount
breadPrice
createdAt
updatedAt</p>
</li>
<li><p><strong>BreadUrl</strong> (빵 사진을 여러장 올릴 수 있도록, 해당 breadIdx당 첫 번째 Url이 대표 사진이 됨)
breadIdx - Bread 테이블에서 참조
breadImgUrl - text</p>
</li>
<li><p><strong>Review</strong> 
reviewIdx
userIdx - User 테이블에서 참조
shopIdx - Shop 테이블에서 참조
(breadIdx - 특정 빵에 대해 리뷰를 쓸 수 있을 때만)
reviewContent - text
createdAt
updatedAt</p>
</li>
<li><p><strong>ReviewUrl</strong>
reviewIdx - Review 테이블에서 참조
reviewImgUrl - text</p>
</li>
</ul>
<h3 id="2-aquerytool을-이용해-table-시각화하기">2) AQueryTool을 이용해 Table 시각화하기</h3>
<p><img src="https://velog.velcdn.com/images/bora_u/post/52bc7ad3-0aae-40d7-a986-45b789a37988/image.png" alt=""></p>
<h3 id="3-추가-기능-및-아이디어">3) 추가 기능 및 아이디어</h3>
<ol>
<li>판매량을 어떻게 나타낼 것인가?</li>
</ol>
<p>-&gt; 어플에서는 판매되고 있는 빵에 대한 정보만 제공할 뿐, 어플을 통해서는 구매할 수 없다.
매장에서 카운트 할 수는 있음.</p>
<ol start="2">
<li><p>빵집 별로 타임 세일을 일주일 평균 몇 번하는지 나타내는 지표가 있으면 좋을 것 같다. ex) 이 가게는 일주일 간 타임 세일을 N번 진행했습니다!</p>
</li>
<li><p>내가 좋아하는 빵집 알림만 받을 수 있도록, 즐겨찾기 기능</p>
</li>
<li><p>리뷰 추천 기능</p>
</li>
</ol>
<p>-&gt; 특정 빵에 대한 리뷰만 쓸 수 있는가? -&gt; 여기서 의문, 1번에서 말했듯이 푸쉬 알람만 울릴 것이 아니라 빵을 앱에서 구매할 수 있어야 함 또는 구매 했음을 앱에서 알 수 있어야 함.. 영수증 리뷰 식으로?</p>
<ol start="5">
<li><p>특정 빵을 많이 구매한 사람에게는, 리뷰 작성 시 나타나는 칭호 제공 ex) 크로와상의 제왕, 단팥빵의 여왕... </p>
</li>
<li><p>빵집 계정 같은 경우, 관리자가 권한을 설정해줌. 따라서 회원가입 때 따로 빵집 인증이 필요 하지 않음</p>
</li>
</ol>
<h2 id="3-느낀-점">3. 느낀 점</h2>
<p>DB 설계를 직접 해보니, 어플의 정확한 기능에 대해 놓친 부분을 깨달을 수 있었습니다. 아마 개발을 계속 해가면서 추가 기능도 보충하고 부족한 부분도 개선해 갈 것 같습니다! DB 설계가 백엔드와 프론트엔드 모두에게 영향을 끼치기도 하고 프로젝트 전반적으로 가장 중요한 작업 중 하나이다보니 시간을 많이 소비하더라도 꼼꼼하게 진행하기로 하였습니다. 그래서 PM님과 제가 각각 설계를 해보고 부족한 점을 보완하기로 하였습니다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring(스프링)과 Spring Boot(스프링 부트) 알아보기]]></title>
            <link>https://velog.io/@bora_u/Spring%EA%B3%BC-Spring-Boot-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@bora_u/Spring%EA%B3%BC-Spring-Boot-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Mon, 04 Jul 2022 09:04:53 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요! 보라입니다💜 </p>
<p>얼마 전 UMC(University Makeus Challenge) 서버 10주차 트랙이 끝났는데요! 지금은 트랙의 연장 선인 앱 개발 프로젝트를 위한 팀이 빌딩 되었고, 이제 막 프로젝트 초반 단계에 있습니다😃 </p>
<p>사실 트랙을 진행하는 동안 저는 Spring과 Spring boot라는 용어들을 혼용하여 사용했습니다. 정확한 개념을 모르고 있었기 때문입니다. 그래서 오늘의 포스팅은 Spring과 Spring boot에 대해 다루어 보겠습니다😁</p>
<h2 id="1-spring스프링">1. Spring(스프링)</h2>
<p><img src="https://velog.velcdn.com/images/bora_u/post/422503e1-eb77-4d5b-904d-210dc7fa99bc/image.png" alt=""></p>
<p>Spring은 JAVA를 기반으로 하는 웹 어플리케이션 프레임워크입니다. (비슷한 프레임워크로는, Python을 기반으로 하는 Django, Javascript를 기반으로 하는 Node.js 등이 있습니다.)</p>
<h3 id="spring스프링의-특징">Spring(스프링)의 특징</h3>
<ul>
<li><p>기본적으로 Spring은 JAVA 객체들과 라이브러리들을 관리해주고, 톰캣이라는 WAS(Web Application Server)가 내장되어 있어 자바 웹 어플리케이션 구동이 가능합니다.</p>
</li>
<li><p>Spring은 경량 컨테이너이며, 자바 객체를 직접 Spring 안에서 관리합니다. 객체의 생성 및 소멸과 같은 생명 주기(Life cycle)을 관리하며, Spring 컨테이너에서 필요한 객체를 가져와 사용합니다. 여기서 중요한 두 개념이 등장합니다.</p>
</li>
<li><p><strong>IOC (Inversion Of Control, 제어의 역전)</strong>
  사용자의 제어권을 다른 주체에게 넘기는 것을 의미합니다.</p>
<p>  일반적으로 자바 프로그램에서는 각 객체들이 프로그램의 흐름을 결정하였고, 각 객체를 직접 생성하고 조작하는 등, 모든 작업을 사용자가 제어하는 구조였습니다.</p>
<p>  하지만 IOC가 적용된 Spring에서는 객체의 생성을 특별한 관리 위임 주체(Spring)에게 맡깁니다. 사용자는 객체를 직접 생성하지 않게 되고, 관리 위임 주체가 객체의 생명주기를 컨트롤하게 됩니다.  </p>
</li>
<li><p><strong>DI (Dipendency Injection, 의존성 주입)</strong>
  객체를 외부에서 생성해서 사용하려는 주체 객체에게 주입해주는 방식을 의미합니다. (서비스와 컴포넌트도 마찬가지!)</p>
<p>  예를 들면, MyController에서 MyService 객체를 사용하고 싶을 때, 일반적인 자바 프로그램에서는 MyController 안에서 MyService 인스턴스를 만들어 사용했을 것입니다. 하지만, DI가 적용된 Spring에서는 MyService 객체를 외부(Spring)에서 생성하고, 주체 객체인 MyController에 주입시켜 사용합니다. </p>
<p>  이러한 특징들은 객체끼리의 의존성을 낮춥니다. 의존성이 낮아진다는 것은 변경 사항이 생겨도 서로에게 영향을 많이 주지 않는다는 것을 의미하며, 이로 인해 단위테스트가 용이해집니다.</p>
</li>
<li><p>DJ (Dipendency Lookup, 의존성 검색)
  DI와는 반대로, 필요한 객체를 주체 객체에 주입 받는 방식이 아닌, 주제 객체에서 직접 검색하는 방식을 의미합니다.</p>
<p>  이때, 주체 객체는 인터페이스 타입을 지정해서 검색할 뿐, 해당 인터페이스를 구현한 인스턴스에 대한 결정과 생명 주기는 IOC 컨테이너에서 책임집니다.</p>
</li>
</ul>
<h2 id="2-spring-boot스프링-부트">2. Spring Boot(스프링 부트)</h2>
<p><img src="https://velog.velcdn.com/images/bora_u/post/faf5dcef-a622-4f20-80a1-0b12a8780693/image.png" alt=""></p>
<p>Spring Boot는 Spring을 사용할 때 사용자가 설정해야하는 부분을 자동화하여 더 쉽게 사용할 수 있도록 해주는 서브 프로젝트입니다.</p>
<p>기존의 Spring은 설정할 것이 너무 많았고, 의존성 관리가 힘들며, 배포가 어렵다는 문제점들이 있었습니다.</p>
<p>이러한 문제점을 해결하기 위해 등장한 것이 Spring Boot입니다. AutoConfiguration Annotation(자동 설정 주석, @ 기호를 앞에 붙여 사용함)을 이용하여 자동 설정을 할 수 있습니다.</p>
<p>다음 포스팅에서는 이러한 스프링 어노테이션에 대해서 자세하게 다루어보겠습니다!</p>
<h3 id="참고-및-출처">참고 및 출처</h3>
<p><a href="https://melonicedlatte.com/2021/07/11/174700.html">https://melonicedlatte.com/2021/07/11/174700.html</a>
<a href="https://hello-bryan.tistory.com/319">https://hello-bryan.tistory.com/319</a>
<a href="https://velog.io/@dusdn2424/%EC%8A%A4%ED%94%84%EB%A7%81-Spring-IoC-DI-DL-%EA%B0%9C%EB%85%90-%EC%A0%95%EB%A6%AC">https://velog.io/@dusdn2424/%EC%8A%A4%ED%94%84%EB%A7%81-Spring-IoC-DI-DL-%EA%B0%9C%EB%85%90-%EC%A0%95%EB%A6%AC</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[UMC 10주차] JWT 적용 키워드]]></title>
            <link>https://velog.io/@bora_u/UMC-10%EC%A3%BC%EC%B0%A8-JWT-%EC%A0%81%EC%9A%A9-%ED%82%A4%EC%9B%8C%EB%93%9C</link>
            <guid>https://velog.io/@bora_u/UMC-10%EC%A3%BC%EC%B0%A8-JWT-%EC%A0%81%EC%9A%A9-%ED%82%A4%EC%9B%8C%EB%93%9C</guid>
            <pubDate>Fri, 27 May 2022 20:45:33 GMT</pubDate>
            <description><![CDATA[<h1 id="핵심-키워드">핵심 키워드</h1>
<h3 id="paging">Paging</h3>
<p>paging이란 page 처리를 해주는 것을 말하는데, 쉽게 말해 데이터 양을 잘라서 출력하는 것이다.</p>
<p>구글에 어떤 검색어를 입력했을 때, 엄청 많은 결과가 나올 것이다.</p>
<p>이때 모든 결과를 한 페이지에 담으려고 하면 페이지의 크기도 너무 커지고 로딩 시간도 길어질 것이다.</p>
<p>그래서 paging을 이용하여 한 페이지에 나오는 데이터의 양을 제한한다.</p>
<p>예를 들어, 데이터 베이스에서 아래 코드를 실행시켰을 때,</p>
<pre><code class="language-sql">SELECT *
FROM User
LIMIT 0,5;</code></pre>
<p>1페이지에는 idx 0부터 5개의 데이터를 출력해준다.</p>
<p>이때 시작 데이터의 idx를 offset이라고 한다.</p>
<h3 id="transaction">Transaction</h3>
<p>Transaction이란 데이터베이스의 상태를 변화시키기 해서 수행하는 작업의 단위를 말한다.</p>
<p>은행에서 송금을 할 때를 생각해보자.
A에서 출금 하여 B에게 보내려는데, 출금만 된 시점에서 에러가 발생한다면 A의 돈이 공중분해 된다.
그래서 출금과 입금 자체를 하나의 단위로 만들고, 둘 다 무사히 실행되었을 때 데이터 베이스의 상태가 변화 되게 된다.</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/1610ffee-b29a-430e-85b5-17fa96a055cb/image.png" alt="">
이때 <code>commit</code>과 <code>rollback</code> 연산은 transaction의 성공과 취소를 반영하는 명령어이다.
트랜잭션 안의 여러개의 연산을 수행하고 있는 상태가 Active 상태이다.
위에서 부분 완료는 명령어 중 몇 개만 성공했다는 뜻이 아니라, 모든 명령어를 성공한 후 commit을 하기 직전의 상태를 말한다.</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/0d6fd95d-29d8-47f4-a542-b411ecf65897/image.png" alt=""></p>
<ul>
<li>원자성 : 하나의 작업단위로 여겨진 모든 명령은 전부 수행되거나, 전부 수행되지 않아야 함.</li>
<li>일관성 : A가 B에게 송금하기 전과 후의 잔액의 합은 같아야 함.</li>
<li>고립성 : A가 B에게 송금하는 트랜잭션이 진행되는 도중에 다른 트랜잭션이 해당 데이터에 접근을 못하도록 해야함.</li>
<li>지속성 : 트랜잭션이 성공한 후라면, 컴퓨터가 꺼져버린다고 해도 그대로 보존 됨. 중간에 꺼졌다면 보존 안됨.</li>
</ul>
<p>출처: <a href="https://mommoo.tistory.com/62">https://mommoo.tistory.com/62</a> [개발자로 홀로 서기:티스토리]</p>
<h3 id="stateless무상태성">Stateless(무상태성)</h3>
<p>HTTP 프로토콜은 요청에 대한 응답을 수행할 뿐, 특정 상태를 저장하지 않는다.</p>
<p>이것을 stateless라고 한다. 다른 예로는 UDP가 있다.</p>
<p>stateless 형태의 경우 로그인을 한다고 해도 로그인을 한 순간 딱 한 번만 로그인이 입증 되고, 그 후에는 상태를 저장하지 않기 때문에 로그인이 유지되지 않는다.</p>
<p>그러나 우리가 사용하는 어플리케이션에서는 로그인이 유지되어야 하기 때문에 여러가지 방법을 써서 상태를 저장한다.</p>
<h3 id="쿠키-vs-세션">쿠키 vs 세션</h3>
<p>그 중 한 가지 방법이 쿠키와 세션을 이용하는 것이다.</p>
<p><strong>쿠키(Cookie)</strong>는 웹 사이트에 접속할 때 생성되는 정보를 담은 임시 파일!
서버가 사용자의 웹 브라우저에 저장하는 데이터를 말한다.
즉, 쿠키는 <strong>사용자</strong>가 가지고 있다가 사용자가 요청을 할 때 쿠키 정보를 함께 보내서 사용자를 식별할 수 있게 한다.</p>
<p><strong>세션(Session)</strong>은 웹 브라우저 당 1개씩 생성되며 웹 컨테이너에 저장되어 브라우저 종료 시 함께 소멸된다.
쿠키와 달리 세션은 <strong>서버</strong>가 가지고 있는다. 서버의 쿠키라고 생각할 수 있다.
ID, 닉네임 등을 세션에 담아두면 요청이 있을 때마다 DB에 접근하지 않아도 되기 때문에 효율적이다!</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/d77b923b-7b7f-44ea-920f-f3620746c8a1/image.png" alt=""></p>
<p>출처 : <a href="https://devuna.tistory.com/23">https://devuna.tistory.com/23</a></p>
<h3 id="jwtjson-web-token">JWT(Json Web Token)</h3>
<p>또 다른 방법은 JWT를 이용하는 것이다.</p>
<p>JWT는 유저를 인증하고 식별하기 위한 토큰 기반 인증 방법이다.
이 토큰에는 사용자의 권한 정보나 서비스를 이용하기 위한 정보가 담겨있다.</p>
<p>Header(인코딩 방식), Payload(사용자의 정보), Signature(전자 서명 즉, 암호화 된 키)로 구성된다.</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/525a7774-69f2-4c94-bb24-feab6700def8/image.png" alt=""></p>
<p>토큰은 클라이언트에 저장되기 때문에 메모리나 스토리지 등을 통해 세션을 관리하던 서버의 부담을 덜 수 있다.</p>
<ul>
<li>JWT의 일반적인 로직</li>
</ul>
<ol>
<li>클라이언트 측에서 아이디, 패스워드를 통해 인증</li>
<li>서버에서 JWT를 생성하여 클라이언트에게 응답으로 돌려줌</li>
<li>클라이언트는 이제 어떤 요청을 할 때마다 헤더에 JWT를 첨부함</li>
<li>서버에서 클라이언트가 보낸 JWT를 검증하고 응답</li>
</ol>
<p>다만 보안이 취약해서 HTTPS가 필수로 요구된다.</p>
<h3 id="oauth-원리와-과정">OAuth 원리와 과정</h3>
<p>OAuth는 인터넷 사용자들이 비밀번호를 제공하지 않고 다른 웹사이트 상의 자신들의 정보에 대해 웹사이트나 애플리케이션의 접근 권한을 부여할 수 있는 공통적인 수단으로서 사용되는 프로토콜이다.</p>
<p>쉽게 설명하자면, 카카오 계정으로 로그인 등과 같이 간편히 회원가입 할 수 있는 기능을 구현할 때 사용되는 프로토콜이다.</p>
<p>보안이 좋지만 복잡한 과정을 가지고 있다.</p>
<p>설명을 찾아보다가, 설명이 정말 잘 되어 있는 블로그를 찾아서 아래 링크를 첨부한다.</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/10dca1db-2a5c-4dd1-a51e-27cfcd6ac107/image.png" alt=""></p>
<p>출처: <a href="https://inpa.tistory.com/entry/WEB-%F0%9F%93%9A-OAuth-20-%EA%B0%9C%EB%85%90-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC">https://inpa.tistory.com/entry/WEB-📚-OAuth-20-개념-💯-정리</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[UMC 10주차] JWT 적용 실습! - 로그인]]></title>
            <link>https://velog.io/@bora_u/UMC-10%EC%A3%BC%EC%B0%A8-JWT-%EC%A0%81%EC%9A%A9-%EC%8B%A4%EC%8A%B5-%EB%A1%9C%EA%B7%B8%EC%9D%B8</link>
            <guid>https://velog.io/@bora_u/UMC-10%EC%A3%BC%EC%B0%A8-JWT-%EC%A0%81%EC%9A%A9-%EC%8B%A4%EC%8A%B5-%EB%A1%9C%EA%B7%B8%EC%9D%B8</guid>
            <pubDate>Fri, 27 May 2022 19:14:08 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요 보라입니다😀 벌써 UMC 강의의 마지막 주차가 되었네요!</p>
<p>바로 시작하겠습니다!</p>
<blockquote>
<ol>
<li>실습 과정</li>
<li>트러블 슈팅</li>
<li>강의 후기</li>
</ol>
</blockquote>
<h1 id="1-실습-과정">1. 실습 과정</h1>
<h3 id="jwt-로그인-api">jwt 로그인 API</h3>
<h4 id="코드-작성">코드 작성</h4>
<p>이번에는 jwt를 이용하여 로그인 API를 만들어보겠다!</p>
<p>먼저 <code>PostLoginReq</code>와 <code>PostLoginRes</code>모델을 만들어주자.</p>
<ul>
<li>model</li>
</ul>
<pre><code class="language-java">//PostLoginReq
@Getter
@Setter
@AllArgsConstructor
public class PostLoginReq {
    private String email;
    private String pwd;

}


//PostLoginRes
@Getter
@Setter
@AllArgsConstructor
public class PostLoginRes {
    private int userIdx;
    private String jwt;

}</code></pre>
<ul>
<li>UserController</li>
</ul>
<pre><code class="language-java">//UserController
    @ResponseBody
    @PostMapping(&quot;/login&quot;)
    public BaseResponse&lt;PostLoginRes&gt; logIn(@RequestBody PostLoginReq postLoginReq) {
        try{
            //email 입력이 되었는지 확인
            if(postLoginReq.getEmail() == null) {
                return new BaseResponse&lt;&gt;(POST_USERS_EMPTY_EMAIL);
            }
            //pwd 입력이 되었는지 확인
            if(postLoginReq.getPwd() == null) {
                return new BaseResponse&lt;&gt;(POST_USERS_EMPTY_PASSWORD);
            }
            //email 정규식 검증
            if(!isRegexEmail(postLoginReq.getEmail())) {
                return new BaseResponse&lt;&gt;(POST_USERS_INVALID_EMAIL);
            }

            PostLoginRes postLoginRes = userService.logIn(postLoginReq);

            return new BaseResponse&lt;&gt;(postLoginRes);

        } catch(BaseException exception){
            return new BaseResponse&lt;&gt;((exception.getStatus()));
        }
    }</code></pre>
<ul>
<li>UserDAO</li>
</ul>
<pre><code class="language-java">//UserDAO
    public User getPwd(PostLoginReq postLoginReq) {
        String getPwdQuery = &quot;SELECT userIdx, name, nickName, email, pwd from User where email = ?&quot;;
        String getPwdParams = postLoginReq.getEmail();

        return this.jdbcTemplate.queryForObject(getPwdQuery,
                (rs,rowNum)-&gt; new User (
                        rs.getInt(&quot;userIdx&quot;),
                        rs.getString(&quot;name&quot;),
                        rs.getString(&quot;nickName&quot;),
                        rs.getString(&quot;email&quot;),
                        rs.getString(&quot;pwd&quot;)
                ),
                getPwdParams
        );
    }</code></pre>
<ul>
<li><p>UserService</p>
<pre><code class="language-java">//UserService
  public PostLoginRes logIn(PostLoginReq postLoginReq) throws BaseException {
      User user = userDao.getPwd(postLoginReq);
      String encryptPwd;

      try{
          //비밀번호 암호화
          encryptPwd = new SHA256().encrypt(postLoginReq.getPwd());

</code></pre>
</li>
</ul>
<pre><code>    } catch(Exception exception){
        throw new BaseException(PASSWORD_ENCRYPTION_ERROR);
    }

    //암호화된 비밀번호와 비교
    if(user.getPwd().equals(encryptPwd)) {
        int userIdx = user.getUserIdx();
        String jwt = jwtService.createJwt(userIdx);
        return new PostLoginRes(userIdx, jwt);
    }
    else {
        throw new BaseException(FAILED_TO_LOGIN);
    }
}</code></pre><pre><code>
* jwtService
jwtService는 이 강의 파일을 내려 받았을 때 이미 구현이 되어있던 코드인데, 아래 코드를 보면 ```JWT_SECRET_KEY```라는 것이 있다. 현재는 config.secret 경로에 임의로 암호화 KEY가 작성되어 있고 이 KEY는 절대 노출되면 안된다! 깃허브에 올릴 때도 조심하도록 하자~

```java
 public String createJwt(int userIdx){
        Date now = new Date();
        return Jwts.builder()
                .setHeaderParam(&quot;type&quot;,&quot;jwt&quot;)
                .claim(&quot;userIdx&quot;,userIdx)
                .setIssuedAt(now)
                .setExpiration(new Date(System.currentTimeMillis()+1*(1000*60*60*24*365)))
                .signWith(SignatureAlgorithm.HS256, Secret.JWT_SECRET_KEY)
                .compact();
    }</code></pre><h4 id="postman-테스트">POSTMAN 테스트!</h4>
<p>POSTMAN에서 jwt 로그인 테스트를 해보기 전에, 비밀번호가 암호화 된 계정이 하나 있어야 하므로 회원가입을 진행해주었다.</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/c8ceac8d-680a-40ba-8788-d38c773bef5f/image.png" alt=""></p>
<p>그리고 테스트를 해보았다.</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/1066abb8-594b-4b2d-a6a8-f7dd196a20a0/image.png" alt=""></p>
<p>성공!</p>
<h3 id="jwt-헤더">JWT 헤더</h3>
<h4 id="코드-작성-1">코드 작성</h4>
<ul>
<li>PostController
기존에 작성되어있던 PostController에 코드를 추가해주었다.</li>
</ul>
<pre><code class="language-java">//PostController
    @ResponseBody
    @PostMapping(&quot;&quot;)
    public BaseResponse&lt;PostPostsRes&gt; createPost(@RequestBody PostPostsReq postPostsReq) {
        try{
            //*****여기부터 추가된 코드*****
            //jwt
            int userIdxByJwt = jwtService.getUserIdx();
            //맞는지 검증
            if(postPostsReq.getUserIdx() != userIdxByJwt) {
                return new BaseResponse&lt;&gt;(BaseResponseStatus.INVALID_USER_JWT);
            }
            //*****여기까지 추가된 코드*****

            //게시물 본문의 길이가 너무 길 때
            if(postPostsReq.getContent().length() &gt; 450) {
                return new BaseResponse&lt;&gt;(BaseResponseStatus.POST_POSTS_INVALID_CONTENTS);
            }
            //사진이 선택되지 않았을 때
            if(postPostsReq.getPostImgUrls().size() &lt; 1) {
                return new BaseResponse&lt;&gt;(BaseResponseStatus.POST_POSTS_EMPTY_IMGURL);
            }

            PostPostsRes postPostsRes = postService.createPost(postPostsReq.getUserIdx(), postPostsReq);
            return new BaseResponse&lt;&gt;(postPostsRes);

        } catch(BaseException exception){
            return new BaseResponse&lt;&gt;((exception.getStatus()));
        }
    }</code></pre>
<h4 id="postman-테스트-1">POSTMAN 테스트</h4>
<p>저번 주차에 만들었던 게시물 생성 API 코드를 다시 테스트해보자!</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/968219f1-cb49-452b-84b3-74f1960c5597/image.png" alt=""></p>
<p>위와 같이 jwt를 입력하라고 뜬다.</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/2ea98314-fa70-4f66-8300-a10d2e36e3be/image.png" alt=""></p>
<p>위처럼 헤더에 jwt 키를 추가해주었다.</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/cd9589d1-1d63-48a2-ac07-fa47fe63baae/image.png" alt=""></p>
<p>그러면 Body result에서는 null이라고 뜨긴 하는데, db로 들어가보면 요청이 잘 수행되었다!</p>
<p>아래는 Post 테이블이고, 가장 아래에 방금 만든 게시물 데이터가 생겼다 ㅎㅅㅎ
<img src="https://velog.velcdn.com/images/bora_u/post/85299e66-f055-460b-9939-a4d59c76f1c0/image.png" alt=""></p>
<p>방금 테스트를 할 때 jwt 값을 살짝 바꿔주었을 때는 유효하지 않는 jwt라고 뜬다.</p>
<p>구현 성공!</p>
<h1 id="2-트러블-슈팅">2. 트러블 슈팅</h1>
<h3 id="문제-원인">문제 원인</h3>
<p>jwt 로그인 테스트를 위해 회원가입을 하려고 했는데 에러가 발생했다.</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/4f81ad81-3cc2-43d7-ac2b-9b4bd3456c09/image.png" alt=""></p>
<h3 id="해결-방안">해결 방안</h3>
<p>알고 보니, 내 DB에서는 column을 pwd라고 만들어주었는데 여기저기서 password와 pwd를 번갈아 사용하다보니 헷갈린 거였다! password를 pwd라고 고쳐주었다.</p>
<p>그랬더니...</p>
<h3 id="문제-원인-1">문제 원인</h3>
<p><img src="https://velog.velcdn.com/images/bora_u/post/d2b2962a-1133-404c-bb53-9bf52200ba65/image.png" alt=""></p>
<p>아래 코드에서 에러가 난 것 같았다.
<img src="https://velog.velcdn.com/images/bora_u/post/4d90116d-46bb-4126-9e77-d72c38f2d647/image.png" alt=""></p>
<h3 id="해결-방안-1">해결 방안</h3>
<p>알고보니 pwd가 그대로 db에 들어가는 것이 아니라, 암호화 처리가 되어서 엄청 길게 들어가기 때문에 내 db에 설정되어있는 pwd varchar(20) 가지고는 택도 없었던 것이었다. pwd의 타입을 TEXT로 수정해주니 해결되었다!</p>
<h1 id="3-강의-후기">3. 강의 후기</h1>
<p>한 학기 동안 많은 걸 하려고 욕심을 냈다 보니, 조금 버거운 적도 있었던 것 같다. 학교 강의들(3학년이다보니 전공도 많이 듣고 과제도 많았는데 그나마 듣는 교양도 수학이라서...), 학생회 집부 활동, UMC 강의 그리고 주짓수까지.. 솔직히 주짓수에 가장 열심히 임한 것 같다 ㅋㅋㅋㅋ 공부도 중간고사 때까지는 열심히 했는데 그 이후로 의욕을 잃은 듯하다...^^</p>
<p>그리고 Spring boot를 처음 다루어 보는데 좋은 강의를 만나서 정말 의미있는 경험이었고, 이 지식들을 바탕으로 더 쉽게 웹 개발을 할 수 있을 것 같다. 내 자신에게 솔직해지자면 최선을 다하지 못한 주차도 있었고, 에러 해결도 못해서 계속 다음 주로 다다음 주로 미루기도 했지만, 모든 경험들이 다 다음 프로젝트 때 거름이 될 것 같다!!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[UMC 9주차] 인스타그램 개발 실전! - POST, PATCH, DELETE]]></title>
            <link>https://velog.io/@bora_u/UMC-9%EC%A3%BC%EC%B0%A8-%EC%9D%B8%EC%8A%A4%ED%83%80%EA%B7%B8%EB%9E%A8-%EA%B0%9C%EB%B0%9C-%EC%8B%A4%EC%A0%84-POST-PATCH-DELETE</link>
            <guid>https://velog.io/@bora_u/UMC-9%EC%A3%BC%EC%B0%A8-%EC%9D%B8%EC%8A%A4%ED%83%80%EA%B7%B8%EB%9E%A8-%EA%B0%9C%EB%B0%9C-%EC%8B%A4%EC%A0%84-POST-PATCH-DELETE</guid>
            <pubDate>Tue, 17 May 2022 19:59:55 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요! 9주차로 돌아온 보라입니다💜</p>
<blockquote>
<ol>
<li>실습 과정</li>
<li>트러블 슈팅</li>
<li>강의 후기</li>
</ol>
</blockquote>
<p>8주차에서 데이터 베이스 연결 오류가 뜨는데, 아직도 해결을 못했습니다😥 어디가 잘못된건지 계속 찾아보고는 있는데 뭔가 예감 상 오타일 것 같긴 합니다😭 그래도 진도는 나가야하니까, 9주차 워크북 시작하겠습니다!</p>
<h1 id="실습-과정">실습 과정</h1>
<h3 id="1-post---게시물-생성-api">1. POST - 게시물 생성 API</h3>
<h4 id="코드-작성">코드 작성</h4>
<p>POST method를 이용하여 게시글을 생성하는 API를 만들어보자!</p>
<p>먼저 아래와 같이 <code>PostPostsReq</code>와 <code>PostImgsUrlReq</code> 모델을 만들어주었다! </p>
<pre><code class="language-java">@Getter
@Setter
@AllArgsConstructor
public class PostPostsReq {
    private int userIdx;
    private String content;
    private List&lt;PostImgsUrlReq&gt; postImgUrls;

}</code></pre>
<pre><code class="language-java">@Getter
@Setter
@AllArgsConstructor
public class PostImgsUrlReq {
    private String imgUrl;
}</code></pre>
<hr>

<ul>
<li>PostController
PostController에서 게시글 생성 함수인 <code>createPosts</code>를 만들어보자.
PostService의 <code>createPosts</code>함수를 사용할 것이다.</li>
</ul>
<pre><code class="language-java">//PostController
    @ResponseBody
    @PostMapping(&quot;&quot;)
    public BaseResponse&lt;PostPostsRes&gt; createPosts(@RequestBody PostPostsReq postPostsReq) {
        try{
            //게시물 본문의 길이가 너무 길 때
            if(postPostsReq.getContent().length() &gt; 450) {
                return new BaseResponse&lt;&gt;(BaseResponseStatus.POST_POSTS_INVALID_CONTENTS);
            }
            //사진이 선택되지 않았을 때
            if(postPostsReq.getPostImgUrls().size() &lt; 1) {
                return new BaseResponse&lt;&gt;(BaseResponseStatus.POST_POSTS_EMPTY_IMGURL);
            }

            PostPostsRes postPostsRes = postService.createPosts(postPostsReq.getUserIdx(), postPostsReq);
            return new BaseResponse&lt;&gt;(postPostsRes);

        } catch(BaseException exception){
            return new BaseResponse&lt;&gt;((exception.getStatus()));
        }
    }
</code></pre>
<hr>

<ul>
<li>PostService
PostService에도 <code>createPost</code> 함수를 만들어주자.
PostService에서는 PostDao의 <code>insertPost</code>함수와 <code>insertPostImgs</code>함수를 사용할 것이다.</li>
</ul>
<pre><code class="language-java">public PostPostsRes createPost(int userIdx, PostPostsReq postPostsReq) throws BaseException {
        try{
            int postIdx = postDao.insertPost(userIdx, postPostsReq.getContent());

            for (int i=0; i&lt;postPostsReq.getPostImgUrls().size(); i++) {
                postDao.insertPostImgs(postIdx, postPostsReq.getPostImgUrls().get(i));
            }

            return new PostPostsRes(postIdx);
        }
        catch (Exception exception) {
            throw new BaseException(DATABASE_ERROR);
        }
    }
</code></pre>
<hr>

<ul>
<li>PostDao
<code>insertPost</code>함수와 <code>insertPostImgs</code> 함수를 만들어주었다.
PostDao에서 만드는 함수에는 쿼리문이 들어가있는 것을 볼 수 있다.
즉, Dao는 직접적으로 DB를 건드리는 부분이다.</li>
</ul>
<pre><code class="language-java">public int insertPost(int userIdx, String content){
        String insertPostQuery = &quot;INSERT INTO Post(useridx, content) VALUES (?, ?)&quot;;
        Object []insertPostParams = new Object[] {userIdx, content};
        this.jdbcTemplate.update(insertPostQuery,
                insertPostParams);

        //자동으로 가장 마지막에 들어간 idx 값을 리턴해줌
        String lastInsertIdxQuery=&quot;SELECT last_insert_id()&quot;;
        return this.jdbcTemplate.queryForObject(lastInsertIdxQuery, int.class);

    }

    public int insertPostImgs(int postIdx, PostImgsUrlReq postImgsUrlReq){
        String insertPostImgsQuery = &quot;INSERT INTO PostImgUrl(postidx, imgUrl) VALUES (?, ?)&quot;;
        Object []insertPostImgsParams = new Object[] {postIdx, postImgsUrlReq.getImgUrl()};
        this.jdbcTemplate.update(insertPostImgsQuery,
                insertPostImgsParams);

        //자동으로 가장 마지막에 들어간 idx 값을 리턴해줌
        String lastInsertIdxQuery=&quot;SELECT last_insert_id()&quot;;
        return this.jdbcTemplate.queryForObject(lastInsertIdxQuery, int.class);

    }</code></pre>
<h4 id="postman-테스트">POSTMAN 테스트</h4>
<p>밑에 null이라고 뜨긴 하는데, DB에 들어가서 확인해보니 데이터가 잘 들어가있었다!</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/448b42e2-06d8-4703-8015-6edd6baf5794/image.png" alt=""></p>
<h3 id="2-patch---게시물-수정-api">2. PATCH - 게시물 수정 API</h3>
<h4 id="코드-작성-1">코드 작성</h4>
<p>이번에는 PATCH method를 이용하여 게시글을 수정하는 API를 만들어보자!</p>
<p>이번에도 아래와 같이 <code>PatchPostsReq</code> 모델을 만들어주었다! </p>
<pre><code class="language-java">@Getter
@Setter
@AllArgsConstructor
public class PatchPostsReq {
    private int userIdx;
    private String content;
    private List&lt;PostImgsUrlReq&gt; postImgUrls;

}</code></pre>
<hr>

<ul>
<li>PostController
PostController에는 게시글 생성 함수인 <code>modifyPost</code>를 만들어보자.
PostService의 <code>modifyPost</code>함수를 사용할 것이다.</li>
</ul>
<pre><code class="language-java">//PostController
    @ResponseBody
    @PatchMapping(&quot;/{postIdx}&quot;)
    public BaseResponse&lt;String&gt; modifyPost(@PathVariable (&quot;postIdx&quot;) int postIdx, @RequestBody PatchPostsReq patchPostsReq) {
        try{
            //게시물 본문의 길이가 너무 길 때
            if(patchPostsReq.getContent().length() &gt; 450) {
                return new BaseResponse&lt;&gt;(BaseResponseStatus.POST_POSTS_INVALID_CONTENTS);
            }

            postService.modifyPost(patchPostsReq.getUserIdx(), postIdx, patchPostsReq);
            String result = &quot;게시물 정보 수정을 완료하였습니다.&quot;;

            return new BaseResponse&lt;&gt;(result);

        } catch(BaseException exception){
            return new BaseResponse&lt;&gt;((exception.getStatus()));
        }
    }</code></pre>
<hr>

<ul>
<li>PostService
PostService에도 <code>modifyPost</code> 함수를 만들어주었다.
PostService에서는 PostDao의 <code>updatePost</code>함수를 사용할 것이다.</li>
</ul>
<pre><code class="language-java">    public void modifyPost(int userIdx, int postIdx, PatchPostsReq patchPostsReq) throws BaseException {
        //존재하지 않는 유저일 경우
        if(postProvider.checkUserExist(userIdx) == 0) {
            throw new BaseException(USERS_EMPTY_USER_ID);
        }
        //존재하지 않는 게시물일 경우
        if(postProvider.checkPostExist(userIdx) == 0) {
            throw new BaseException(POSTS_EMPTY_POST_ID);
        }

        try {
            int result = postDao.updatePost(postIdx, patchPostsReq.getContent());

            //업데이트 과정 중 문제가 생겼을 경우
            if(result == 0) {
                throw new BaseException(MODIFY_FAIL_POST);
            }
        }
        catch (Exception exception) {
            throw new BaseException(DATABASE_ERROR);
        }
    }</code></pre>
<p>위의 코드처럼 validation 처리를 해주기 위해 에러 코드를 추가해준 후, PostDao와 PostProvider에 각각 <code>checkPostExist</code> 함수를 추가해주었다.</p>
<pre><code class="language-java">//BaseResponseStatus
//에러 코드 추가
POSTS_EMPTY_POST_ID(false, 2020, &quot;게시물 아이디 값을 확인해주세요.&quot;),

MODIFY_FAIL_POST(false, 3020, &quot;게시물 수정을 실패했습니다.&quot;),</code></pre>
<pre><code class="language-java">//PostDao
//Post가 존재하는지 확인하는 checkPostExist 함수
public int checkPostExist(int postIdx){
        String checkPostExistQuery = &quot;select exists(select postIdx from Post where postIdx = ?)&quot;;
        int checkPostExistParams = postIdx;
        return this.jdbcTemplate.queryForObject(checkPostExistQuery,
                int.class,
                checkPostExistParams);

    }</code></pre>
<pre><code class="language-java">//PostPovider
//Post가 존재하는지 확인하는 checkPostExist 함수
public int checkPostExist(int postIdx) throws BaseException{
        try{
            return postDao.checkPostExist(postIdx);
        } catch (Exception exception){
            throw new BaseException(DATABASE_ERROR);
        }
    }</code></pre>
<hr>

<ul>
<li>PostDao
<code>updatePost</code>함수를 만들어주었다.</li>
</ul>
<pre><code class="language-java">    public int updatePost(int postIdx, String content){
        String updatePostQuery = &quot;UPDATE Post SET content=? WHERE postIdx=?&quot;;
        Object [] updatePostParams = new Object[] {content, postIdx};
        return this.jdbcTemplate.update(updatePostQuery, updatePostParams);

    }</code></pre>
<h4 id="postman-테스트-1">POSTMAN 테스트</h4>
<p><img src="https://velog.velcdn.com/images/bora_u/post/eccf6206-d35d-43bc-a074-7c58c75fc3c7/image.png" alt=""></p>
<h3 id="3-patch---게시글-삭제-api">3. PATCH - 게시글 삭제 API</h3>
<h4 id="코드-작성-2">코드 작성</h4>
<p>이번에는 PATCH를 이용해서 게시글을 삭제하는 API를 만들어보겠다.</p>
<p>게시글 삭제인데 PATCH method를 이용하는 이유는, 게시글이 삭제된다는 것이 DB에서 완전히 사라지는 것을 의미하는 것이 아닌, status가 &#39;ACTIVE&#39;에서 &#39;INACTIVE&#39;로 변화하는 것을 의미하기 때문이다.</p>
<p>삭제는 전달 받을 것이 없으므로 model은 따로 만들어주지 않아도 된다!</p>
<ul>
<li>PostController
PostController에는 게시글 생성 함수인 <code>deletePost</code>를 만들어보자.
PostService의 <code>deletePost</code>함수를 사용할 것이다.</li>
</ul>
<pre><code class="language-java">//PostController
    @ResponseBody
    @PatchMapping(&quot;/{postIdx}/status&quot;)
    public BaseResponse&lt;String&gt; deletePost(@PathVariable (&quot;postIdx&quot;) int postIdx) {
        try{
            postService.deletePost(postIdx);
            String result = &quot;삭제를 성공했습니다.&quot;;

            return new BaseResponse&lt;&gt;(result);

        } catch(BaseException exception){
            return new BaseResponse&lt;&gt;((exception.getStatus()));
        }
    }</code></pre>
<hr>

<ul>
<li>PostService
PostService에도 <code>deletePost</code> 함수를 만들어주었다.
PostService에서는 PostDao의 <code>deletePost</code>함수를 사용할 것이다.</li>
</ul>
<pre><code class="language-java">//PostService
    public void deletePost(int postIdx) throws BaseException {
        try {
            int result = postDao.deletePost(postIdx);

            //삭제 과정 중 문제가 생겼을 경우
            if(result == 0) {
                throw new BaseException(DELETE_FAIL_POST);
            }
        }
        catch (Exception exception) {
            throw new BaseException(DATABASE_ERROR);
        }
    }</code></pre>
<p>위의 코드처럼 validation 처리를 해주기 위해 에러 코드를 추가해주었다.</p>
<pre><code class="language-java">//BaseResponseStatus
//에러 코드 추가
DELETE_FAIL_POST(false,3021,&quot;게시물 삭제를 실패했습니다.&quot;),</code></pre>
<hr>

<ul>
<li>PostDao
<code>deletePost</code>함수를 만들어주었다.</li>
</ul>
<pre><code class="language-java">//PostDao
    public int deletePost(int postIdx){
        String deletePostQuery = &quot;UPDATE Post SET status=&#39;INACTIVE&#39; WHERE postIdx=?&quot;;
        Object [] deletePostParams = new Object[] {postIdx};
        return this.jdbcTemplate.update(deletePostQuery, deletePostParams);

    }</code></pre>
<h4 id="postman-테스트-2">POSTMAN 테스트</h4>
<p><img src="https://velog.velcdn.com/images/bora_u/post/660bf1d7-11e7-4315-9ae2-91eb82f54911/image.png" alt=""></p>
<h1 id="트러블-슈팅">트러블 슈팅</h1>
<h2 id="postman-테스트-오류-1">POSTMAN 테스트 오류 1</h2>
<h3 id="문제-원인">문제 원인</h3>
<p>게시물 생성 API를 테스트하는데 에러가 떴다.</p>
<p><code>Request method &#39;POST&#39; not supported</code> 라고 하는데!
<img src="https://velog.velcdn.com/images/bora_u/post/f36a1d26-16c7-436c-bbe3-0599b75d7fc8/image.png" alt=""></p>
<h3 id="해결-방안">해결 방안</h3>
<p>바보같이 빌드를 안 해서 오류가 떴었답니다..허허 </p>
<h2 id="postman-테스트-오류-2">POSTMAN 테스트 오류 2</h2>
<h3 id="문제-원인-1">문제 원인</h3>
<p>게시물 수정, 삭제 API를 테스트하는데 에러가 떴다.</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/f2a1d475-f7a0-4188-be06-bc77ba46c59c/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/b18a9cb8-5e2c-49ba-b90e-a63b2dcbb503/image.png" alt=""></p>
<h3 id="해결-방안-1">해결 방안</h3>
<p>여기도 빌드 안 해서..ㅎ</p>
<h1 id="강의-후기">강의 후기</h1>
<p>포스트맨 테스트를 다 실패해서 실습을 하긴 했지만 한 것 같지가 않다... 허허
그리고 에러 로그로 뜨다 보니까 어디에서 틀렸는지 정확히 알려주지를 않아서 화가 난다!!</p>
<p>코드 이해도 제대로 해보고 싶은데, 진도도 나가야하고 할 일도 있다 보니 아직 깊이 이해하지는 못했다 ㅠㅠ 하지만 시간이 생기면 꼭 해볼 것이다.</p>
<p>그리고 혼자서 이정도 수준의 개발을 할 수 있을까 생각을 해봤는데 무리일 것 같다.. 더 깊이 공부해보려는 자세가 필요할 것 같다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[UMC 8주차] 인스타그램 개발 실전! - GET 사용하기]]></title>
            <link>https://velog.io/@bora_u/UMC-8%EC%A3%BC%EC%B0%A8-%EC%9D%B8%EC%8A%A4%ED%83%80%EA%B7%B8%EB%9E%A8-%EA%B0%9C%EB%B0%9C-%EC%8B%A4%EC%A0%84-GET-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@bora_u/UMC-8%EC%A3%BC%EC%B0%A8-%EC%9D%B8%EC%8A%A4%ED%83%80%EA%B7%B8%EB%9E%A8-%EA%B0%9C%EB%B0%9C-%EC%8B%A4%EC%A0%84-GET-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 11 May 2022 19:23:25 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요! 보라입니다💜 </p>
<p>벌써 8주차라니! 두 달이 정말 빠르게 지나갔던 것 같습니다.</p>
<p>저도 이제 바쁜 일들이 거의 끝나서, 공부에만 집중할 수 있게 되었답니다~!</p>
<p>코로나로 인해 잠시 쉬었던 운동도 다시 다니고 있고, 알바를 그만 두니 주말에 여유 시간이 생겨 너무 좋습니다😊</p>
<p>당장 급한 일을 처리하느라 살짜쿵 미뤄 놓았던 강의들만 조금.. 남은 상태입니다 호호</p>
<p>이번 주말을 활용하여 하긴 했지만, 꼼꼼히 하지 못 했던! 공부들을 해보려고 합니다😙</p>
<p>이번 주차 워크북 시작합니당!</p>
<blockquote>
<ol>
<li>실습 과정</li>
<li>트러블 슈팅</li>
<li>강의 후기</li>
</ol>
</blockquote>
<h1 id="실습-과정">실습 과정</h1>
<h3 id="1-유저-피드-조회-api">1. 유저 피드 조회 API</h3>
<h4 id="코드-작성">코드 작성</h4>
<ul>
<li>model</li>
</ul>
<p>유저의 피드를 생각해보자.
나의 피드인지, 다른 사람의 피드인지에 따라 구성이 다르므로 구별하는 변수를 만들어준다.
또 피드에는 유저의 정보와 유저의 게시글들이 있는데, 다소 복잡하기 때문에 각각 객체 정보로 만들어주었다.
이때, 한 명의 유저는 여러 개의 게시글을 작성했을 수 있으므로 List로 만들어준다!</p>
<pre><code class="language-java">@Getter
@Setter
@AllArgsConstructor
public class GetUserFeedRes {
    private boolean _isMyFeed; //내 피드인지 다른 사람의 피드인지 구별 하는 변수
    private GetUserInfoRes getUserInfo;
    private List&lt;GetUserPostsRes&gt; getUserPosts;

}</code></pre>
<p>피드를 나타내기 위한 유저 정보에는 무엇이 필요할까?
아래와 같이 만들어주었다.</p>
<pre><code class="language-java">@Getter
@Setter
@AllArgsConstructor
public class GetUserInfoRes {
    private String nickName;
    private String name;
    private String profileImgUrl;
    private String website;
    private String introduce;
    private int followerCount;
    private int followingCount;
    private int postCount;
}</code></pre>
<p>마지막으로 유저가 작성한 게시글 정보이다. 게시글의 번호와 이미지가 필요하다.</p>
<pre><code class="language-java">public class GetUserPostsRes {
    private int postIdx;
    private String postImgUrl;

}</code></pre>
<ul>
<li>UserController<pre><code class="language-java">  //유저 피드 조회
  @ResponseBody
  @GetMapping(&quot;/{userIdx}&quot;) // (GET) 127.0.0.1:9000/users
  public BaseResponse&lt;GetUserFeedRes&gt; getUserFeed(@PathVariable(&quot;userIdx&quot;) int userIdx) {
      try{
          GetUserFeedRes getUserFeedRes = userProvider.retrieveUserFeed(userIdx, userIdx);
          return new BaseResponse&lt;&gt;(getUserFeedRes);
      } catch(BaseException exception){
          return new BaseResponse&lt;&gt;((exception.getStatus()));
      }
  }</code></pre>
</li>
</ul>
<ul>
<li><p>UserDao
유저 정보를 받아오는 함수</p>
<pre><code class="language-java">public GetUserInfoRes selectUserInfo(int userIdx){
      String selectUserInfoQuery = &quot;SELECT u.userIdx as userIdx,&quot; +
              &quot;       u.nickName as nickName, &quot; +
              &quot;       u.name as name, &quot; +
              &quot;       u.profileImgUrl as profileImgUrl, &quot; +
              &quot;       u.website as website, &quot; +
              &quot;       u.introduce as introduce,\n&quot; +
              &quot;        IF(postCount is null, 0, postCount) as postCount,\n&quot; +
              &quot;        IF(followerCount is null, 0, followerCount) as followCount,\n&quot; +
              &quot;        IF(followingCount is null, 0, followingCount) as followingCount\n&quot; +
              &quot;FROM User as u\n&quot; +
              &quot;    left join (SELECT userIdx, COUNT(postIdx) as postCount\n&quot; +
              &quot;                FROM Post\n&quot; +
              &quot;             WHERE status = &#39;ACTIVE&#39;\n&quot; +
              &quot;             GROUP BY userIdx) p on p.userIdx = u.userIdx\n&quot; +
              &quot;    left join (SELECT followerIdx, COUNT(followIdx) as followerCount\n&quot; +
              &quot;                FROM Follow\n&quot; +
              &quot;                WHERE status = &#39;ACTIVE&#39;\n&quot; +
              &quot;                GROUP BY followerIdx) f1 on f1.followerIdx = u.userIdx\n&quot; +
              &quot;    left join (SELECT followeeIdx, COUNT(followIdx) as followingCount\n&quot; +
              &quot;                FROM Follow\n&quot; +
              &quot;                WHERE status = &#39;ACTIVE&#39;\n&quot; +
              &quot;                GROUP BY followeeIdx) f2 on f2.followeeIdx = u.userIdx\n&quot; +
              &quot;\n&quot; +
              &quot;WHERE u.userIdx = ? and u.status = &#39;ACTIVE&#39;;&quot;;

      int selectUserInfoParam=userIdx;

      return this.jdbcTemplate.queryForObject(selectUserInfoQuery,
              (rs,rowNum) -&gt; new GetUserInfoRes(
                      rs.getString(&quot;nickName&quot;),
                      rs.getString(&quot;name&quot;),
                      rs.getString(&quot;profileImgUrl&quot;),
                      rs.getString(&quot;website&quot;),
                      rs.getString(&quot;introduce&quot;),
                      rs.getInt(&quot;followerCount&quot;),
                      rs.getInt(&quot;followingCount&quot;),
                      rs.getInt(&quot;postCount&quot;)
              ),selectUserInfoParam);
  }</code></pre>
<p>그리고! 유저의 게시물 리스트를 받아오는 함수</p>
</li>
</ul>
<pre><code class="language-java">    public List&lt;GetUserPostsRes&gt; selectUserPosts(int userIdx){
        String selectUserPostsQuery =
                &quot;SELECT p.postIdx as postIdx,\n&quot; +
                &quot;       pi.imgUrl as postImgUrl\n&quot; +
                &quot;FROM Post as p\n&quot; +
                &quot;    join User as u on u.userIdx = p.userIdx\n&quot; +
                &quot;    join PostImgUrl as pi on pi.postIdx = p.postIdx and pi.status = &#39;ACTIVE&#39;\n&quot; +
                &quot;WHERE p.status = &#39;ACTIVE&#39; AND u.userIdx = ?\n&quot; +
                &quot;GROUP BY p.postIdx\n&quot; +
                &quot;ORDER BY p.createdAt desc;&quot;;

        int selectUserPostsParam=userIdx;

        return this.jdbcTemplate.query(selectUserPostsQuery,
                (rs,rowNum) -&gt; new GetUserPostsRes(
                        rs.getInt(&quot;postIdx&quot;),
                        rs.getString(&quot;postImgUrl&quot;)

        ),selectUserPostsParam);
    }</code></pre>
<p>작성하다보니, 유저 id가 정말 존재하는지 확인하는 validation이 필요하다!</p>
<p>그래서 UserDao에는 이렇게
<img src="https://velog.velcdn.com/images/bora_u/post/dd0610e2-9688-4897-9e6e-915dcacb7981/image.png" alt="">
UserProvider에는 이렇게 만들어주었다.
<img src="https://velog.velcdn.com/images/bora_u/post/d4c75f60-a48c-4e12-9ac6-538ad5382ff8/image.png" alt=""></p>
<h4 id="postman-테스트">POSTMAN 테스트</h4>
<p>//에러 발생</p>
<h4 id="명세서-작성">명세서 작성</h4>
<h3 id="2-게시물-리스트-조회-api">2. 게시물 리스트 조회 API</h3>
<h4 id="코드-작성-1">코드 작성</h4>
<p>게시물 리스트 조회 API를 만들기 위해서는 src 아래 경로에 post라는 새로운 패키지를 만들어줘야한다.</p>
<p>그래서 아래와 같이 만들어주었고, Post를 위한 Controller, Dao, Provider, Service도 만들어주었다. User의 코드를 참고했다!</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/f4fda8cc-9855-4ba2-9594-497b5b07151d/image.png" alt=""></p>
<ul>
<li>model<pre><code class="language-java">//GetPostImgRes 파일
@Getter
@Setter
@AllArgsConstructor
public class GetPostImgRes {
  private int postImgIdx;
  private String imgUrl;
</code></pre>
</li>
</ul>
<p>}</p>
<p>//GetPostsRes 파일
@Getter
@Setter
@AllArgsConstructor
public class GetPostsRes {
    private int postIdx;
    private int userIdx;
    private String nickName;
    private String profileImgUrl;
    private String content;
    private int postLikeCount;
    private int commentCount;
    private String updateAt;
    private String likeOrNot;
    private List<GetPostImgRes> imgs;
}</p>
<pre><code>
* PostController
```java
//PostController 파일 안에 작성
    @ResponseBody
    @GetMapping(&quot;&quot;) // 쿼리스트링
    public BaseResponse&lt;List&lt;GetPostsRes&gt;&gt; getPosts(@RequestParam int userIdx) {
        try{
            List&lt;GetPostsRes&gt; getPostsRes = postProvider.retrievePosts(userIdx);
            return new BaseResponse&lt;&gt;(getPostsRes);
        } catch(BaseException exception){
            return new BaseResponse&lt;&gt;((exception.getStatus()));
        }
    }</code></pre><ul>
<li><p>PostProvider</p>
<pre><code class="language-java">//PostProvider 파일 안에 작성
  public List&lt;GetPostsRes&gt; retrievePosts(int userIdx) throws BaseException {
      Boolean isMyFeed = true;

      if(checkUserExist(userIdx) == 0) {
          throw new BaseException(USERS_EMPTY_USER_ID);
      }
      try{
          List&lt;GetPostsRes&gt; getPosts = postDao.selectPosts(userIdx);

          return getPosts;
      }
      catch (Exception exception) {
          throw new BaseException(DATABASE_ERROR);
      }
  }

  //userid가 존재하는지 체크하는 함수
  public int checkUserExist(int userIdx) throws BaseException{
      try{
          return postDao.checkUserExist(userIdx);
      } catch (Exception exception){
          throw new BaseException(DATABASE_ERROR);
      }
  }</code></pre>
</li>
<li><p>PostDao</p>
<pre><code class="language-java">  //피드를 보여주는 화면에서 필요한 데이터들을 받아와주는 함수
  public List&lt;GetPostsRes&gt; selectPosts(int userIdx){
      String selectPostsQuery =
              &quot;SELECT p.postIdx as postIdx,\n&quot; +
                      &quot;       u.userIdx as userIdx,\n&quot; +
                      &quot;       u.nickName as nickName,\n&quot; +
                      &quot;       u.profileImgUrl as profileImgUrl,\n&quot; +
                      &quot;       p.content as content,\n&quot; +
                      &quot;       IF(postLikeCount is null, 0, postLikeCount) as postLikeCount,\n&quot; +
                      &quot;       IF(commnentCount is null, 0, commentCount) as commmentCount,\n&quot; +
                      &quot;       CASE WHEN timestampdiff(second, p.updatedAt, current_timestamp) &lt; 60\n&quot; +
                      &quot;        THEN concat(timestampdiff(second, p.updatedAt, current_timestamp), &#39;초 전&#39;)\n&quot; +
                      &quot;        WHEN timestampdiff(minute, p.updatedAt, current_timestamp) &lt; 60\n&quot; +
                      &quot;        THEN concat(timestampdiff(minute, p.updatedAt, current_timestamp), &#39;분 전&#39;)\n&quot; +
                      &quot;        WHEN timestampdiff(hour, p.updatedAt, current_timestamp) &lt; 24\n&quot; +
                      &quot;        THEN concat(timestampdiff(hour, p.updatedAt, current_timestamp), &#39;시간 전&#39;)\n&quot; +
                      &quot;        WHEN timestampdiff(day, p.updatedAt, current_timestamp) &lt; 365\n&quot; +
                      &quot;        THEN concat(timestampdiff(day, p.updatedAt, current_timestamp), &#39;일 전&#39;)\n&quot; +
                      &quot;        ELSE timestampdiff(year, p.updatedAt, current_timestamp)\n&quot; +
                      &quot;        END AS updatedAt,\n&quot; +
                      &quot;        IF(pl.status = &#39;ACTIVE&#39;, &#39;Y&#39;, &#39;N&#39;) as likeOrNot\n&quot; +
                      &quot;        FROM Post as p\n&quot; +
                      &quot;        JOIN User as u on u.userIdx = p.useridx\n&quot; +
                      &quot;        LEFT JOIN (SELECT postIdx, userIdx, count(postLikeidx) as postLikeCount\n&quot; +
                      &quot;                   FROM PostLike\n&quot; +
                      &quot;                   WHERE status =&#39;ACTIVE&#39;\n&quot; +
                      &quot;                   GROUP BY postIdx) as pl on p.postIdx = pl.postIdx\n&quot; +
                      &quot;        LEFT JOIN (SELECT postIdx, COUNT(commentIdx) as commentCount\n&quot; +
                      &quot;                   FROM Comment\n&quot; +
                      &quot;                   WHERE status=&#39;ACTIVE&#39;) as c on c.postIdx = p.postIdx\n&quot; +
                      &quot;        WHERE p.status = &#39;ACTIVE&#39; and p.postIdx = ?&quot;;

      int selectPostsParam=userIdx;

      return this.jdbcTemplate.query(selectPostsQuery,
              (rs,rowNum) -&gt; new GetPostsRes(
                      rs.getInt(&quot;postIdx&quot;),
                      rs.getInt(&quot;userIdx&quot;),
                      rs.getString(&quot;nickname&quot;),
                      rs.getString(&quot;profileImgUrl&quot;),
                      rs.getString(&quot;content&quot;),
                      rs.getInt(&quot;postLikeCount&quot;),
                      rs.getInt(&quot;commentCount&quot;),
                      rs.getString(&quot;updatedAt&quot;),
                      rs.getString(&quot;likeOrNOt&quot;),
                      getPostImgRes=this.jdbcTemplate.query(&quot;\&quot;SELECT pi.postImgUrlIdx, pi.imgUrl +\n&quot; +
                              &quot;              \&quot;               FROM PostImgUrl as pi\&quot; +\n&quot; +
                              &quot;              \&quot;               JOIN Post as p on p.postIdx = pi.postIdx +\n&quot; +
                              &quot;              \&quot;               WHERE pi.status = &#39;ACTIVE&#39; and p.postIdx = ?;\&quot;&quot;,
                              (rk,rownum) -&gt; new GetPostImgRes(
                                      rk.getInt(&quot;postImgUrlIdx&quot;),
                                      rk.getString(&quot;imgUrl&quot;)
                              ), rs.getInt(&quot;postIdx&quot;)
                              )

              ),selectPostsParam);
  }

  //userid가 존재하는지 체크하는 함수
  public int checkUserExist(int userIdx){
      String checkUserExistQuery = &quot;select exists(select userIdx from User where userIdx = ?)&quot;;
      int checkUserExistParams = userIdx;
      return this.jdbcTemplate.queryForObject(checkUserExistQuery,
              int.class,
              checkUserExistParams);

  }</code></pre>
</li>
</ul>
<h4 id="postman-테스트-1">POSTMAN 테스트</h4>
<h4 id="명세서-작성-1">명세서 작성</h4>
<h1 id="트러블-슈팅">트러블 슈팅</h1>
<h4 id="문제-원인">문제 원인</h4>
<p>Postman에서 테스트를 하려고 했는데 오류가 발생했다.</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/67e683ab-01aa-44ff-a957-3803fba5fab0/image.png" alt="">
Database 에러라는 것을 보아, Provider 쪽 에러 같았다.</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/a6d41241-ffed-4798-bd5d-c7384f247746/image.png" alt=""></p>
<h4 id="해결-방안">해결 방안</h4>
<h1 id="강의-후기">강의 후기</h1>
<p>이번 강의는 본격적으로 코딩이 포함되어 있었던 것 같다! 사실 백엔드 개발자로 진로를 정하게 된 계기가 2학년 때 인터넷 프로그래밍 과목에서 웹 프로젝트를 했었던 것인데, 이번 주차 실습을 하면서 그때 생각이 났다 ㅎㅎ</p>
<p>인프 과목을 듣고 웹 개발에 흥미가 생겨서 프로젝트도 해보고 이것저것 해봤었던 게 기억이 난다. 그때는 그저 교수님이 가르쳐주시는 대로 따라하기만 했고, 따라하더라도 시간이 부족해서 Controller, Dao, Provider, Service 등을 왜 만드는지, 함수가 어떻게 동작하는지도 이해하지 못 했었다. 이제는 본격적으로, 나에게 맞는 분야를 찾아 자발적으로 공부를 해야하는 3학년이 되었고 지금 다시 접했을 땐 그때보다 수월하게 이해할 수 있었다.</p>
<p>그래도 아직은 조금 이해가 안되는 부분이 있긴 하다. 이번 주말에 시간이 널널하니 README 파일도 다시 한 번 읽어보면서 제대로 포스팅하게 될 것 같다!</p>
<p>그리고 실습하면서 느낀 건데, 인텔리제이 버그 너무 많고 진짜 구린 것 같다 ㅎㅎ😊😍 (근데 이클립스는 더 구림ㅋㅋ) 솔직히 내가 실수해서 오류 뜬 걸 인텔리제이 버그라고 생각해서 화난 적도 있긴 한데 인텔리제이 버그 뜰 때마다 진짜 화면 뿌시고 싶었다 ㅎㅎ😋✨(대충 그지같은 말을 예쁘게 쓰면 예쁜 말이 된다고 믿는 사람) 근데 이건 인텔리제이 입장도 들어봐야 될 듯 합니다 ㅋ</p>
<p>8주차가 되기까지 쉽게 온 것 같진 않지만, 제가 관심있는 분야를 공부할 수 있는 기회를 얻어서 행운이라고 생각합니다!! 다음주도 파이팅~</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[UMC 7주차] Spring boot 프레임 워크 사용하기 - API 실습]]></title>
            <link>https://velog.io/@bora_u/UMC-7%EC%A3%BC%EC%B0%A8-Spring-boot-%ED%94%84%EB%A0%88%EC%9E%84-%EC%9B%8C%ED%81%AC-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0-%EC%8B%A4%EC%8A%B5</link>
            <guid>https://velog.io/@bora_u/UMC-7%EC%A3%BC%EC%B0%A8-Spring-boot-%ED%94%84%EB%A0%88%EC%9E%84-%EC%9B%8C%ED%81%AC-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0-%EC%8B%A4%EC%8A%B5</guid>
            <pubDate>Wed, 11 May 2022 17:45:21 GMT</pubDate>
            <description><![CDATA[<blockquote>
<ol>
<li>실습 과정</li>
<li>트러블 슈팅</li>
</ol>
</blockquote>
<h1 id="실습-과정">실습 과정</h1>
<h4 id="인텔리제이-db-연결">인텔리제이 DB 연결!</h4>
<p>환경 구축에서 했던 것과 마찬가지로, 인텔리제이에서도 build와 자바 실행을 해주었다.</p>
<p>조금 시간이 걸렸지만 결국 성공!
<img src="https://velog.velcdn.com/images/bora_u/post/d4b35099-cb78-4b1b-9eb8-8b453946d058/image.png" alt=""></p>
<p>POSTMAN에서 테스트 해보자.</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/6704dcef-68af-450e-a60f-99ee3a4ddb3b/image.png" alt=""></p>
<p>분명 알맞게 입력해주었는데... </p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/bfa82c8d-3157-4f65-aa38-1ef05a489ef3/image.png" alt=""></p>
<p>localhost로 하고 싶으면 다운받으랜다... ㅡ3ㅡ 그래서 다운 받아주고 다시 시도~</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/d2feb159-eb0b-4227-b76f-6540092d56ef/image.png" alt=""></p>
<p>성공! 쭉쭉 잘되니까 기분이가 좋아용😁 서버도 잘 실행되고 있고, 디비 연결도 잘 되었다는 의미!</p>
<h4 id="유저-조회-api">유저 조회 API</h4>
<p>조회(체크) : Provider에서 처리
생성 : Service에서 처리
실질적 쿼리문 : Dao에 작성</p>
<p>UserController에는
<img src="https://velog.velcdn.com/images/bora_u/post/f3d45d51-b54b-4e9e-a5f9-433519d26943/image.png" alt=""></p>
<p>UserDao에는
<img src="https://velog.velcdn.com/images/bora_u/post/fb0c77d9-ab5f-41ce-acbd-7e88a2599fb9/image.png" alt=""></p>
<p>UserProvide에는
<img src="https://velog.velcdn.com/images/bora_u/post/a40b23e5-2829-444b-ab72-e3b94e929e91/image.png" alt=""></p>
<p>이렇게 유저 조회를 위한 API가 만들어져있었다.</p>
<h4 id="챌린지-과제-유저-삭제-api">챌린지 과제! 유저 삭제 API</h4>
<p>위 API를 참고하여 유저 삭제 API를 만들어보겠다.</p>
<p>BaseResponseStatus에서 새로운 status를 만들려고 했는데 이미 있었다!</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/dcc84a0f-f741-4510-9019-66dbcbd3f719/image.png" alt=""></p>
<h1 id="트러블-슈팅">트러블 슈팅</h1>
<h2 id="1-인텔리제이-gradle-문제">1. 인텔리제이 gradle 문제</h2>
<h3 id="문제-원인">문제 원인</h3>
<p>인텔리제이에서 프로젝트를 오픈했는데, 여기 저기 다 빨간 줄이 뜨면서 오류가 발생했다. (사진이 없습니다ㅠㅠ)</p>
<h3 id="해결-방안--참고-자료">해결 방안 &amp; 참고 자료</h3>
<p>먼저, jdk 버전을 맞춰주었다. </p>
<p>인텔리제이 프로젝트에서 <code>.idea</code> 파일을 지운 후 인텔리제이를 종료한 뒤, 다시 build.grade을 open해주었다.</p>
<p>참고 : <a href="https://ksabs.tistory.com/184">https://ksabs.tistory.com/184</a></p>
<h2 id="2-인텔리제이-자바-실행-실패">2. 인텔리제이 자바 실행 실패</h2>
<h3 id="문제-원인-1">문제 원인</h3>
<p>분명 가상 머신에서는 됐었는데 인텔리제이에서 로컬 환경 테스트를 하는데 계속 자바 실행 할 때 연결 실패가 떴다..</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/028827c8-7fe2-4511-b967-bdf4daa88861/image.png" alt=""></p>
<p>알고 보니, 강의를 따라하다 보니 실수로 데이터베이스 이름을 test로 적어논 것..!</p>
<p>그러니까, url의 엔드포인트 뒤에 <code>test?autoRecconect=true</code>를 써놓고 왜 안되지.. 이러고 있었다 ㅋㅎㅋㅎㅋㅎㅋㅎㅋ </p>
<h3 id="해결-방안">해결 방안</h3>
<p>데이터 베이스 이름을 제대로 써줬다 ㅎㅎ.. 그랬더니 연결 완료! (이걸로 30분 헤맸다)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[UMC 7주차] Spring boot 프레임 워크 사용하기 - 환경 구축]]></title>
            <link>https://velog.io/@bora_u/UMC-7%EC%A3%BC%EC%B0%A8-%ED%94%84%EB%A0%88%EC%9E%84-%EC%9B%8C%ED%81%AC-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0-Spring-boot</link>
            <guid>https://velog.io/@bora_u/UMC-7%EC%A3%BC%EC%B0%A8-%ED%94%84%EB%A0%88%EC%9E%84-%EC%9B%8C%ED%81%AC-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0-Spring-boot</guid>
            <pubDate>Sun, 08 May 2022 12:19:46 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요 보라입니다💜</p>
<p>쓰다보니 글의 길이가 길어져서, 하나 더 포스팅 하도록 하겠습니다!</p>
<blockquote>
<ol>
<li>실습 과정</li>
<li>트러블 슈팅</li>
<li>강의 후기 및 반성</li>
</ol>
</blockquote>
<h1 id="실습-과정">실습 과정</h1>
<h3 id="1-환경-구축하기">1. 환경 구축하기</h3>
<h4 id="java-설치">java 설치</h4>
<p>먼저, 서버에 java가 설치되어있지 않기 때문에, 환경을 구축해볼 것이다.</p>
<p>WinSCP로 접속하여 Putty를 실행해주었다.</p>
<pre><code># sudo su
# apt install default-jdk</code></pre><h4 id="깃허브-클론">깃허브 클론</h4>
<p>그 다음은 (컴공 선배에서 제공받은) 깃허브 주소를 이용하여 Spring boot를 설치해보겠다. 클론 하는 방법을 이용할 것이다!</p>
<pre><code># cd /var/www
# git clone [깃허브 주소]</code></pre><h4 id="환경-설정-파일-default-수정">환경 설정 파일 default 수정</h4>
<p><img src="https://velog.velcdn.com/images/bora_u/post/6ee0722d-b22b-424c-a9ef-719f0d0b4f1f/image.png" alt=""></p>
<p>다음으로 vim을 이용해 서버 환경 설정 파일인 default 파일을 수정해보자!</p>
<pre><code>vim /etc/nginx/sites-available/default</code></pre><p>위 명령어를 입력하여 편집기를 켜주고,</p>
<p>아래처럼 방금 설치한 파일 경로로 변경해준다.</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/78d6453c-7b58-494c-b0eb-7430af631b1e/image.png" alt=""></p>
<h4 id="9000번-포트-열어주기">9000번 포트 열어주기</h4>
<p>ec2와 rds 모두 인바운드 규칙 편집을 통해 9000번 포트를 열어주었다.
<img src="https://velog.velcdn.com/images/bora_u/post/f2041a94-9656-4406-909e-c1409870cd07/image.png" alt=""></p>
<h4 id="build-해주기">build 해주기</h4>
<p>이제 이 파일로 이동해서 build를 해줄 예정이다.</p>
<pre><code># cd udemy_server_practice_springboot
# ./gradlew clean build </code></pre><p>아래처럼 뜬다면 빌드가 완료된 것이다!</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/458ef7f1-cdc0-4f5c-a8ec-68aa36e4ec3e/image.png" alt=""></p>
<h4 id="java-실행">java 실행</h4>
<p><code># java -jar build/libs/demo-0.0.1-SNAPSHOT.jar</code></p>
<p>이제 실행해보면 아래처럼 뜨면서 실행이 시작된다. 시간이 조금 걸릴 수 있따... </p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/4cfbf895-ce5d-471f-b629-54e075e9caf4/image.png" alt=""></p>
<p>그런데 중간에 갑자기 멈췄고, 움직이지 않았다..</p>
<p>인스턴스 중지/시작을 한 번 해준 후에 아래 링크를 참고하여 서버 메모리 문제를 해결해주었다.</p>
<p>참고 : <a href="https://seungwoolog.tistory.com/m/68">https://seungwoolog.tistory.com/m/68</a></p>
<p>아래와 같이 swap 공간이 잘 생겼나 확인해준다.</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/8391469c-e24d-4be6-adbe-9a729d0ead30/image.png" alt=""></p>
<p>그리고 실행해보았더니 잘 된다! 아래 같이 뜨면 성공이다 짝짝</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/5b9b6f82-1684-4129-8818-08c504362d1d/image.png" alt=""></p>
<h4 id="https-보안-적용">https 보안 적용</h4>
<p>그리고 SSL 설정을 위해 letsencrypt를 설치해주겠다. </p>
<p><code>apt-get install letsencrypt</code></p>
<h3 id="2-postman에서-테스트-하기">2. POSTMAN에서 테스트 하기</h3>
<h4 id="postman-회원가입">postman 회원가입</h4>
<p>POSTMAN : <a href="https://www.postman.com/">https://www.postman.com/</a></p>
<p>API 테스트를 위해 먼저 postman 사이트에 접속하여 회원가입을 해주었다.</p>
<h4 id="get-테스트">GET 테스트!</h4>
<p>GET 테스트를 할 것이므로, <code>http://www.worldofbora.shop:9000/users/1</code>를 입력해보았다!</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/320c1ac7-2c21-4b3d-a763-781c62eeb389/image.png" alt=""></p>
<p>아래에 요청에 성공하였습니다가 뜨는 모습!</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/b3769287-0d88-400b-9c68-db047490ef34/image.png" alt=""></p>
<h4 id="proxy-설정">proxy 설정</h4>
<p>다시 default 파일을 수정해주어야한다. </p>
<pre><code># vim /etc/nginx/sites-available/default</code></pre><p>아래와 같이 수정해준다!</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/716a3a6c-18df-43bb-8b60-cb44f62fe44e/image.png" alt=""></p>
<ul>
<li>프록시 설정은 왜 할까?
참고 : <a href="https://developerhjg.tistory.com/168">https://developerhjg.tistory.com/168</a></li>
</ul>
<h4 id="무중단-서비스-배포">무중단 서비스 배포</h4>
<pre><code># service nginx restart //서비스 재실행
# nohup java -jar build/libs/demo-0.0.1-SNAPSHOT.jar</code></pre><p>위 코드처럼, 실행 코드 앞에 <code>nohup</code>을 붙여주면 자바가 무중단 서비스로 배포된다! </p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/3ac00797-9ce4-4697-915c-49b3a98552f2/image.png" alt=""></p>
<p>POSTMAN에서 테스트를 해보고 잘 작동이 되낟면 성공한 것이다.</p>
<p>환경 설정 끝!</p>
<h1 id="트러블-슈팅">트러블 슈팅</h1>
<h3 id="문제-원인">문제 원인</h3>
<p>build 중 다음과 같은 에러가 발생했다</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/b7a31768-d9ba-4f18-853a-5dff27dbdf7b/image.png" alt=""></p>
<p>이 에러의 원인은 스프링에서 database를 사용한다고 설정해두고, 정작 datasource는 설정이 되어 있지 않아서 발생하는 것이라고 한다.</p>
<h3 id="해결-방법">해결 방법</h3>
<p>먼저, application.yml에서 DB 리소스 정보를 수정해야한다! 경로는 아래와 같다.</p>
<pre><code># cd /var/www/udemy_server_practice_springboot/src/main/resources
# vim application.yml</code></pre><p><img src="https://velog.velcdn.com/images/bora_u/post/fe22b1fb-6900-4471-83f1-79dfd4e5c48c/image.png" alt=""></p>
<p>vim 편집기를 켜보니 이렇게 datasource 부분이 비어 있었다.
<img src="https://velog.velcdn.com/images/bora_u/post/7bcfb279-0afb-4f09-8288-cfed0d9c8ab5/image.png" alt=""></p>
<p>내가 접근하려고 하는 db의 정보들을 알맞게 입력해주었다. </p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/c9efb89d-6195-4054-b40c-6d198ad904e3/image.png" alt=""></p>
<p>그 후 다시 build 해주면 된다!</p>
<h4 id="잠시-멍청한-보라-썰">잠시 멍청한 보라 썰</h4>
<p>여기서 잠깐... 제가 멍청했습니다!!!! 알맞게 입력했는데도(알맞게 입력했다고 생각했는데도 ㅋㅋ) 계속 오류가 떠서, ERROR 부분 밑으로 코드를 싹 다 읽어보았습니다. 그런데.. 이런 문장 발견...!</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/4c820d63-2b0d-42c9-9842-ffeb81dbf018/image.png" alt=""></p>
<p>그렇습니다... 저는 datagrip에 boraDB를 만든 적이 없었습니다. boraServer를 만들었었죠... 정말 허무했지만 해결하게 되어 너무 기쁩니다😂</p>
<h3 id="참고-자료">참고 자료</h3>
<p>저와 함께 스터디를 진행하고 있는 팀원 &#39;크롱&#39;의 게시글을 참고하였습니다💜</p>
<h1 id="강의-후기-및-반성">강의 후기 및 반성</h1>
<p>여기까지 강의를 들었을 때, 강의에서 생략된 부분이 있었는데 그 부분에서 오류가 많이 났던 것 같다. 그런 부분들을 하나씩 트러블 슈팅을 해가면서 해결해야 했는데, 이 실습을 할 때의 내가 너무 많이 지쳐있었다... 결국 에러를 해결하지 못 한 채로 스터디를 하게 되었고, 팀원들에게 양해를 구하게 되었다. </p>
<p>다시 에러를 해결하는 과정에서 우리 스터디 팀의 팀장이신, &#39;크롱&#39;의 블로그를 보게 되었다. 그리고 스스로 많이 반성하게 되었다. 나도 에러를 해결하기 위해 디스코드 채널 내에 있는 &#39;지식in&#39; 서버를 탐색했었는데, 크롱의 닉네임이 많이 보였던 것이 기억이 났다. 크롱에게도 정말 많은 에러가 발생했지만, 어떻게든 노력하여 해결하는 과정이 크롱의 블로그에 담겨있었다. 나는 그보다 못 한 노력으로 에러를 해결하지 못 했다고 징징댔던 것 같다. 이 강의를 듣는 사람들의 백그라운드 지식은 다 다르겠지만, 우선 다 같은 강의를 듣는 상황에서 나에게 어려운 것은 다른 사람에게도 어려웠을 것인데... 다른 사람은 나보다 훨씬 많은 노력을 해서 해결했다는게 충격이었다. 그러니까 내가 해결하지 못 한 것은 그만큼 노력하지 않아서이다. </p>
<p>정말 부끄러운 건 포기하고 싶다는 말을 워크북에 적었던 것이다. 개인적인 일정으로 50시간 동안 잠을 잘 수 없었고 당시의 내가 너무 지쳐서 그랬던 것이겠지만... 다들 시간이 넘쳐나서 스터디를 하는 게 아니라, 배우고 성장하기 위해 이 스터디를 하는 것인데.. 특히 서버 팀원들의 워크북을 일일이 확인하며 어떻게든 도움을 주려고 하는 에디에게 이런 모습을 보였다는게 너무 부끄럽다. 스터디를 시작할 때 마음가짐을 잃어버린 것 같아서 다시 나를 돌아보게 되는 계기가 되었다. 이 일을 계기삼아 지난 나의 모습을 반성하고, 얼마 남지 않은 스터디를 잘 마무리 하기 위해 더 열심히 할 것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[UMC 5주차] 데이터 베이스 실습]]></title>
            <link>https://velog.io/@bora_u/UMC-5%EC%A3%BC%EC%B0%A8-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%8B%A4%EC%8A%B5</link>
            <guid>https://velog.io/@bora_u/UMC-5%EC%A3%BC%EC%B0%A8-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%8B%A4%EC%8A%B5</guid>
            <pubDate>Thu, 05 May 2022 21:30:43 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요! 보라입니다! 💜</p>
<p>시험기간과 여러가지 과제가 겹쳐서 정말 바쁜 2주를 보냈습니다..😂</p>
<p>사실 실습은 제때 진행했고 과정까지 다 캡쳐해놓았는데 코멘트를 달지 못 해서 포스팅이 늦어지게 되었습니다.. </p>
<p>비록 늦었지만, 미리 작성해놨던 워크북을 바탕으로 업로드 합니다!! </p>
<blockquote>
<ol>
<li>실습 과정</li>
<li>트러블 슈팅</li>
</ol>
</blockquote>
<h1 id="1-실습-과정">1. 실습 과정</h1>
<h3 id="aws의-rds-기능을-이용하여-데이터-베이스-구축하기">AWS의 RDS 기능을 이용하여 데이터 베이스 구축하기</h3>
<p>먼저 아래처럼 RDS를 검색해준다.
<img src="https://velog.velcdn.com/images/bora_u/post/c9b06192-fdcc-4f15-b9f6-1a1da81763ee/image.png" alt=""></p>
<p>데이터 베이스 생성을 클릭!</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/7f0abc58-7478-455a-95ff-101ddeae0442/image.png" alt=""></p>
<p>MySQL 데이터 베이스를 만들거니까, 제대로 체크 한 후 반드시 프리티어로~
<img src="https://velog.velcdn.com/images/bora_u/post/8b1e8e6f-8316-40a2-8691-9cb5af11144e/image.png" alt=""></p>
<p>그리고 마스터 사용자 이름 &#39;root&#39;를 만들어준다. </p>
<p>비밀번호는 나중에 datagrip 연동할 때 사용되니 절대 잊지 말 것!</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/ef2d1edf-a559-4b48-8af0-f7ccf88df5c3/image.png" alt=""></p>
<p>연결 부분에서 퍼블릭 액세스 &#39;예&#39;로 설정해주기,</p>
<p>보안 그룹에 내가 만든 거 추가해주기!</p>
<p>그 후 데이터베이스 생성 클릭~</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/23683a7b-7032-473d-ba49-ad6c5140a214/image.png" alt=""></p>
<p>그럼 이렇게 뜬다! 조금만 기다려주자~ (5분 정도 걸린 것 같다..)</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/b59fe04d-5c85-4afd-aca3-0b103ae5e2c7/image.png" alt=""></p>
<p>약간의 시간이 지나니 생성에 성공했다!</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/65f17ee0-5487-4a4a-b4f1-86e8da4e0b5e/image.png" alt=""></p>
<p>이제 파라미터 그룹을 만들어주고 (구글링해서 따라하다보니 파라미터 그룹 이름이 이상하다 ㅋㅋ) </p>
<p><code>character_set</code>을 검색한다.
<img src="https://velog.velcdn.com/images/bora_u/post/7f835615-01d9-4dcd-baf1-652f4491dee2/image.png" alt=""></p>
<p>아래와 같이 설정해준다!</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/b352e613-a9ac-42bf-ac21-966c2f068890/image.png" alt=""></p>
<p>마찬가지로 <code>collation</code>을 검색하여 아래와 같이 설정해준다 ㅎㅎ </p>
<p>++ 그리고 사진을 빼먹은 게 있는데 <code>time_zone</code>을 검색하여 <code>Asia/Seoul</code> 로 변경해주어야함!</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/cd08bb0f-0132-4159-92c7-57590a6a56f5/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/a00ebf4c-fb1f-4f65-afd9-4bbe5678d3ef/image.png" alt=""></p>
<p>이렇게 하면 설정은 끝!!</p>
<p>datagrip을 이용하여 쿼리문을 작성할 것이기 때문에 datagrip을 다운받아주었다.</p>
<p>참고 : <a href="https://woong9149.github.io/aws-rds-mysql-db/">https://woong9149.github.io/aws-rds-mysql-db/</a></p>
<p>datagrip에서 우클릭하여 Data Source &gt; MySQL 을 선택해준다.</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/4d962763-e8ac-413f-b35b-9f91699b1555/image.png" alt=""></p>
<p>그리고... 여기까지는 외부에서 실습을 진행하고 있었는데, datagrip과 내가 만든 DB가 연결에 실패하는 불상사가 발생했다..</p>
<p>반쯤 포기하고 새벽에 집에서 다시 시도해보니 성공했는데 그래서인지 실습 사진이 없다😂</p>
<p>워크밴치에서 내가 방금 만든 RDS에 연결한 후 DB를 만들어주고, datagrip에서 해당 DB에 연결하면 된다 ㅎㅎ</p>
<p>위 링크를 참고하여 연결해주었다!!</p>
<h3 id="데이터-베이스-쿼리문-실습">데이터 베이스 쿼리문 실습</h3>
<p>먼저 boraServer 라는 schema를 만들어주었다!</p>
<p>그 후, Aqurey Tool에서 만들었던 테이블 생성 코드를 가져와서 만들어준 상태이다.</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/6bc62648-6a36-423d-bef2-74d9ee6baddf/image.png" alt=""></p>
<p>이런식으로 User 테이블에 더미 데이터를 넣어주겠다!
<img src="https://velog.velcdn.com/images/bora_u/post/3b7971cc-ea10-4763-9a08-3d0148e6541c/image.png" alt=""></p>
<p>Post 테이블에도 넣어주었다.
<img src="https://velog.velcdn.com/images/bora_u/post/3f28e91f-f684-42ea-b5ce-ea2a6d2e9060/image.png" alt=""></p>
<p>userIdx가 2인 유저가 작성한 post를 가져 오고 싶을 때 사용하는 쿼리
<img src="https://velog.velcdn.com/images/bora_u/post/79dbe4bb-8411-403f-ac61-6fa18d0656b0/image.png" alt=""></p>
<p>서브 쿼리문 예시! left join 뒤에 또 쿼리문을 써주었는데, 이것을 서브 쿼리문이라고 한다. 서브 쿼리문은 반드시 이름을 붙여주어야 한다! 강의에 나오는 것처럼 p 라고 지어주었다. </p>
<p>as 명령어를 사용하면 rename 기능을 이용할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/cdb0a2cb-eca2-4a98-96df-48695c587e78/image.png" alt=""></p>
<p>위 쿼리문의 결과이다! 잘 실행이 된다.</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/137357eb-e3e5-4847-a489-9cbfb016850e/image.png" alt=""></p>
<p>새롭게 알게된 사실! 쿼리문에도 IF문을 쓸 수 있다.
<img src="https://velog.velcdn.com/images/bora_u/post/3541a0aa-a26d-4837-93ab-77dab5dc98b1/image.png" alt=""></p>
<p>결과물! NULL로 표시 되었었는데 IF문을 작성해주니 0으로 표시가 잘 되었다.
<img src="https://velog.velcdn.com/images/bora_u/post/f646f78e-e44a-46fc-842f-741525b97261/image.png" alt=""></p>
<p>같은 방법으로 follower 수와 followee 수도 표시할 수 있도록 쿼리를 써주었다. 
<img src="https://velog.velcdn.com/images/bora_u/post/261d25a9-f778-43e7-8da8-4b8039f3858b/image.png" alt=""></p>
<p>다음으로는 아래처럼 PostImgUrl 테이블에 더미 데이터를 추가해준다.
<img src="https://velog.velcdn.com/images/bora_u/post/96253af4-c281-4d86-abf5-5134911c7f9c/image.png" alt=""></p>
<p>새롭게 알게 된 사실! <code>userIdx = ?</code> 라고 쓰면 입력하는 창이 뜬다.</p>
<p>아래와 같이 최근에 올린 게시글 먼저, 게시글들을 모두 보여주는 쿼리문을 작성했다.
<img src="https://velog.velcdn.com/images/bora_u/post/eaca8f5c-198b-436c-b8c5-e36e4926a07c/image.png" alt=""></p>
<h1 id="2-트러블-슈팅">2. 트러블 슈팅</h1>
<h3 id="문제-원인">문제 원인</h3>
<p>datagrip과 DB 연결 중 다음과 같은 에러가 발생했다.</p>
<p><img src="https://velog.velcdn.com/images/bora_u/post/e6710d3b-a250-4f58-a6e4-fc5302d88325/image.png" alt=""></p>
<h3 id="해결-방안">해결 방안</h3>
<p>외부에서 하다가 집으로 돌아와서, 보안 그룹에 인바운드 규칙 추가 &gt; 내 IP를 추가해주었더니 연결에 성공하였다!</p>
<h3 id="참고-자료">참고 자료</h3>
<p>너디너리 디스코드 내 Spring 지식 in 참고💜</p>
]]></description>
        </item>
    </channel>
</rss>