<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>raymond_yoon.log</title>
        <link>https://velog.io/</link>
        <description>흘러가되 원하는 방향으로</description>
        <lastBuildDate>Mon, 01 Jun 2026 05:25:33 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>raymond_yoon.log</title>
            <url>https://velog.velcdn.com/images/raymond_yoon/profile/c7660cc6-94f4-4895-be1c-37c9ca6a6d29/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. raymond_yoon.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/raymond_yoon" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[AI가 코드를 잘 짜게 만드는 기술, 하네스 엔지니어링(Harness Engineering)]]></title>
            <link>https://velog.io/@raymond_yoon/AI%EA%B0%80-%EC%BD%94%EB%93%9C%EB%A5%BC-%EC%9E%98-%EC%A7%9C%EA%B2%8C-%EB%A7%8C%EB%93%9C%EB%8A%94-%EA%B8%B0%EC%88%A0-%ED%95%98%EB%84%A4%EC%8A%A4-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4%EB%A7%81Harness-Engineering</link>
            <guid>https://velog.io/@raymond_yoon/AI%EA%B0%80-%EC%BD%94%EB%93%9C%EB%A5%BC-%EC%9E%98-%EC%A7%9C%EA%B2%8C-%EB%A7%8C%EB%93%9C%EB%8A%94-%EA%B8%B0%EC%88%A0-%ED%95%98%EB%84%A4%EC%8A%A4-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4%EB%A7%81Harness-Engineering</guid>
            <pubDate>Mon, 01 Jun 2026 05:25:33 GMT</pubDate>
            <description><![CDATA[<h1 id="ai가-코드를-잘-짜게-만드는-기술-하네스-엔지니어링harness-engineering">AI가 코드를 잘 짜게 만드는 기술, 하네스 엔지니어링(Harness Engineering)</h1>
<hr>
<h2 id="ai는-생각보다-자주-실수한다">AI는 생각보다 자주 실수한다</h2>
<p>최근 Claude Code 같은 AI 에이전트를 활용해 개발하는 사례가 늘고 있다.</p>
<p>나도 Spring Boot + React 기반 AI 전자책 생성 서비스 Remon을 개발하면서 Claude Code를 꽤 적극적으로 썼는데, 초반에는 같은 요청을 해도 결과가 들쭉날쭉했다.</p>
<p>AI가 반복했던 실수들을 나열해보면 이렇다.</p>
<ul>
<li>백엔드 작업인데 프론트 파일을 건드리거나, 반대로 프론트 작업인데 백엔드 코드를 수정하는 경우</li>
<li><code>Book</code> 엔티티를 Controller에서 그냥 반환해버리는 코드 생성 (DTO 쓰라고 했는데)</li>
<li>커밋 요청도 안 했는데 알아서 커밋하려는 시도</li>
<li>요청하지 않은 리팩토링을 같이 해버려서 기존 구조가 바뀌는 경우</li>
<li>굳이 필요하지 않은 라이브러리를 추가하는 경우</li>
</ul>
<p>처음엔 AI 성능 문제인가 싶었는데, 곰곰이 생각해보니 그게 아니었다.<br>AI가 작업할 환경 자체가 제대로 정의되어 있지 않았던 것이다.</p>
<hr>
<h2 id="하네스-엔지니어링이란">하네스 엔지니어링이란?</h2>
<p>찾아보니 이걸 <strong>하네스 엔지니어링(Harness Engineering)</strong> 이라고 부르더라.</p>
<p>AI에게 좋은 프롬프트를 주는 것과는 다른 개념이다.<br>프로젝트 규칙을 정의하고, 작업 범위를 제한하고, 코드 스타일을 통일하고, 보안 취약점 방지 규칙을 명시해서 AI가 올바른 방향으로 일하도록 <strong>환경 자체를 설계</strong>하는 것이다.</p>
<p>한 마디로, AI한테 일을 맡기기 전에 일하는 방식을 먼저 정해두는 것.</p>
<hr>
<h2 id="claudemd로-작업-환경을-설계하다">CLAUDE.md로 작업 환경을 설계하다</h2>
<p>Remon은 백엔드(Spring Boot), 프론트엔드(React), 앱(React Native) 세 영역으로 나뉜다.<br>각 영역마다 별도의 <code>CLAUDE.md</code>를 만들어서 작업 규칙을 문서로 정리했다.</p>
<h3 id="작업-범위-제한">작업 범위 제한</h3>
<pre><code class="language-md">## 작업 범위
- 이 파일이 있는 backend/ 폴더 내부만 읽고 수정한다.
- 상위 디렉터리나 frontend/는 접근하지 않는다.</code></pre>
<p>이 한 줄이 생각보다 효과가 컸다.<br>백엔드 작업 중에 프론트 파일을 건드리는 문제가 바로 사라졌다.</p>
<hr>
<h3 id="코드-수정-원칙">코드 수정 원칙</h3>
<pre><code class="language-md">## 코드 수정 원칙
1. 파일을 먼저 Read한 뒤 수정한다. 읽지 않은 파일은 수정하지 않는다.
2. 요청된 범위 외 수정(리팩터링, 주석 추가 등)은 하지 않는다.
3. 새 라이브러리는 꼭 필요한 경우에만 추가한다.
4. 보안 취약점(SQL Injection, XSS, 하드코딩된 시크릿 등)을 도입하지 않는다.</code></pre>
<p>특히 2번이 중요했다. AI가 요청하지 않은 리팩토링을 같이 하는 습관이 있었는데, 이 규칙을 명시하고 나서 딱 요청한 것만 처리하게 됐다.</p>
<hr>
<h3 id="커밋-규칙">커밋 규칙</h3>
<pre><code class="language-md">## Git / 커밋 규칙
- 사용자가 명시적으로 요청할 때만 커밋한다.
- 커밋 메시지: feat: / fix: / refactor: / test: / docs: / style:</code></pre>
<p>이게 없을 때는 AI가 알아서 커밋을 해버리거나, 메시지 형식이 매번 달랐다.<br>명시해두고 나서는 내가 원하는 타이밍에, 원하는 형식으로 커밋이 이루어졌다.</p>
<hr>
<h2 id="실제로-달라진-것들">실제로 달라진 것들</h2>
<p>가장 크게 달라진 건 <strong>컨텍스트를 매번 설명하지 않아도 된다</strong>는 점이었다.</p>
<p>CLAUDE.md 적용 전에는 작업을 시작할 때마다 이런 설명을 반복해야 했다.</p>
<blockquote>
<p>&quot;이 프로젝트는 Spring Boot 기반이고, 패키지 구조는 이렇게 되어있고, DTO를 꼭 써야 하고...&quot;</p>
</blockquote>
<p>적용 후에는 Claude Code가 CLAUDE.md를 먼저 읽고 프로젝트 맥락을 유지한 채 작업했다.</p>
<h3 id="사례-1--gemini-api-재시도-로직">사례 1 — Gemini API 재시도 로직</h3>
<p>Gemini API에서 간헐적으로 500/503 오류가 발생했을 때의 일이다.<br>CLAUDE.md에 기술 스택과 코드 원칙이 명시되어 있었기 때문에, AI가 새 클래스를 만들거나 구조를 바꾸지 않고 기존 <code>OpenAiService</code> 안에 재시도 로직을 정확히 추가했다.</p>
<pre><code class="language-java">// maxAttempts=3, 3초 대기 재시도 로직
// 규칙이 없었다면 새 클래스를 만들거나 구조를 바꿨을 가능성이 높다</code></pre>
<h3 id="사례-2--레몬-차감-비관적-락">사례 2 — 레몬 차감 비관적 락</h3>
<p>동시 요청 시 레몬이 중복 차감되는 문제를 해결할 때도 비슷했다.<br><code>@Lock(LockModeType.PESSIMISTIC_WRITE)</code> 위치를 Repository로 정확히 잡고, <code>LemonService</code>의 <code>consumeLemon</code>에만 <code>@Transactional</code>을 명시했다.<br>패키지 구조가 CLAUDE.md에 정의되어 있었기 때문에, AI가 구조를 임의로 바꾸지 않고 기존 흐름 안에서 처리했다.</p>
<hr>
<h2 id="프롬프트보다-중요한-건-컨텍스트">프롬프트보다 중요한 건 컨텍스트</h2>
<p>이걸 적용하면서 가장 크게 느낀 점이다.</p>
<p>좋은 프롬프트 하나를 고민하는 것보다, 프로젝트 구조와 규칙을 문서로 정리해두는 게 훨씬 효과적이었다. 같은 모델인데 환경을 어떻게 설계하느냐에 따라 결과 품질이 완전히 달랐다.</p>
<hr>
<h2 id="마무리">마무리</h2>
<p>AI 도구를 쓰면서 느낀 건, 결국 AI를 잘 쓰는 개발자는 프롬프트를 잘 쓰는 사람이 아니라 <strong>AI가 올바르게 일할 수 있는 환경을 만드는 사람</strong>이라는 것이다.</p>
<p>앞으로 AI 기반 개발이 더 일반화될수록, 이런 환경 설계 능력이 더 중요해질 것 같다.</p>
<p>실제 CLAUDE.md가 궁금하다면 아래 레포에서 확인할 수 있다.</p>
<p>👉 <a href="https://github.com/RaymondYoon/remon-service">https://github.com/RaymondYoon/remon-service</a></p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[👨🏻‍💻Spring에서 MyBatis와 JPA, 무엇을 써야 할까?]]></title>
            <link>https://velog.io/@raymond_yoon/Spring%EC%97%90%EC%84%9C-MyBatis%EC%99%80-JPA-%EB%AC%B4%EC%97%87%EC%9D%84-%EC%8D%A8%EC%95%BC-%ED%95%A0%EA%B9%8C</link>
            <guid>https://velog.io/@raymond_yoon/Spring%EC%97%90%EC%84%9C-MyBatis%EC%99%80-JPA-%EB%AC%B4%EC%97%87%EC%9D%84-%EC%8D%A8%EC%95%BC-%ED%95%A0%EA%B9%8C</guid>
            <pubDate>Fri, 21 Mar 2025 12:57:09 GMT</pubDate>
            <description><![CDATA[<p>Spring으로 백엔드 개발을 하다 보면 자연스럽게 MyBatis와 JPA를 모두 접하게 된다. 처음에는 “뭐가 더 좋은 거지?”, “내가 언제 뭘 써야 할까?” 같은 고민이 있다. </p>
<hr>

<h4 id="mybatis">MyBatis</h4>
<p>장점</p>
<ul>
<li>SQL을 직접 작성 -&gt; 쿼리 제어가 자유롭다</li>
<li>복잡한 JOIN, 동적 쿼리 처리에 강하다</li>
<li>어떤 SQL이 실행되는지 명확하게 보인다</li>
</ul>
<p>단점</p>
<ul>
<li>SQL을 전부 직접 작성해야 해서 생산성이 떨어질 수 있다.</li>
<li>테이블 변경 시 SQL도 수정해야 하므로 유지보수가 힘들 수 있다.</li>
<li>객체지향적인 설계에는 조금 약한 느낌..?<pre><code class="language-xml">&lt;!-- UserMapper.xml --&gt;
&lt;select id=&quot;findByUsername&quot; resultType=&quot;User&quot;&gt;
SELECT * FROM users WHERE username = #{username}
&lt;/select&gt;</code></pre>
<pre><code class="language-java">// UserMapper.java
User findByUsername(String username);</code></pre>
</li>
</ul>
<p>MyBatis는 이런 곳에서 유용한듯</p>
<ul>
<li>통계, 분석 등 복잡한 쿼리가 많은 기능</li>
<li>성능 튜닝이 중요한 조회 중심 서비스</li>
<li>SQL 작성이 익숙한 팀원들과 협업할 때<hr>

</li>
</ul>
<h4 id="jpa">JPA</h4>
<p>장점</p>
<ul>
<li>기본적인 CRUD는 쿼리 없이도 가능 -&gt; 생산성이 좋다</li>
<li>엔티티 중심의 객체지향 설계가 가능하다</li>
<li>지연 로딩, 변경 감지 같은 고급 기능도 지원한다</li>
</ul>
<p>단점</p>
<ul>
<li>처음엔 개념이 많고 어렵다(영속성 컨텍스트, 지연 로딩 등)</li>
<li>복잡한 쿼리 작성이 불편하고 성능 튜닝이 까다로울 수 있음</li>
<li>어떤 쿼리가 나가는지 감이 안 잡힐 때가 있을수도..?<pre><code class="language-java">// UserRepository.java
public interface UserRepository extends JpaRepository&lt;User, Long&gt; {
  Optional&lt;User&gt; findByUsername(String username);
}</code></pre>
</li>
</ul>
<p>JPA는 이런 곳에서 유용한 것 같다.</p>
<ul>
<li>CRUD가 주로 이루어지는 일반적인 웹 서비스</li>
<li>도메인 중심의 객체지향 모델링이 중요한 경우</li>
<li>빠르게 개발하고 유지보수까지 고려해야 할 때<hr>

</li>
</ul>
<h4 id="정리하며">정리하며</h4>
<table>
<thead>
<tr>
<th>항목</th>
<th>MyBatis</th>
<th>JPA</th>
</tr>
</thead>
<tbody><tr>
<td>쿼리 방식</td>
<td>직접 작성</td>
<td>자동 생성 or JPQL</td>
</tr>
<tr>
<td>학습 난이도</td>
<td>쉬운 편</td>
<td>개념이 많아 어려움</td>
</tr>
<tr>
<td>생산성</td>
<td>낮음 (쿼리 직접 작성)</td>
<td>높음 (자동 처리)</td>
</tr>
<tr>
<td>복잡한 쿼리</td>
<td>강함</td>
<td>제한적</td>
</tr>
<tr>
<td>객체지향 설계</td>
<td>약함</td>
<td>강함</td>
</tr>
<tr>
<td>유지보수</td>
<td>어렵게 느껴질 수 있음</td>
<td>구조화되어 편함</td>
</tr>
<tr>
<td><hr></td>
<td></td>
<td></td>
</tr>
</tbody></table>
<h4 id="결국-선택은-상황에-따라서">결국 선택은 상황에 따라서</h4>
<p>개인적으로 CRUD 위주의 서비스는 JPA, 
복잡한 통계성 쿼리나 성능이 중요한 부분은 MyBatis로 나눠서 쓰는게 좋았다.
둘 중 하나만 고집할 필요는 없고, 상황에 따라 선택하거나 혼합해서 사용하는 것도 좋은 방법이었다.</p>
<hr>
<h2 id="그럼-mybatis랑-jpa는-어떻게-돌아가는-걸까">그럼 MyBatis랑 JPA는 어떻게 돌아가는 걸까?</h2>
<ul>
<li><h4 id="mybatis는-xml에-sql을-직접-작성하고-인터페이스랑-연결해서-사용하는-방식이다">MyBatis는 XML에 SQL을 직접 작성하고, 인터페이스랑 연결해서 사용하는 방식이다</h4>
</li>
</ul>
<p><code>resources/mapper/</code> 같은 경로에 XML 파일을 만들고, SQL을 <code>&lt;select&gt;</code>, <code>&lt;insert&gt;</code> 등으로 작성해준다.<br>그리고 인터페이스 파일에서 XML의 <code>id</code> 값과 같은 메서드를 선언하면 된다.</p>
<pre><code class="language-xml">&lt;!-- UserMapper.xml --&gt;
&lt;select id=&quot;findByUsername&quot; resultType=&quot;User&quot;&gt;
  SELECT * FROM users WHERE username = #{username}
&lt;/select&gt;</code></pre>
<pre><code class="language-java">// UserMapper.java
@Mapper
public interface UserMapper {
    User findByUsername(String username);
}</code></pre>
<p>이렇게 매핑하면 UserMapper의 메서드를 호출할 때 XML 안의 쿼리가 실행된다.
SQL을 내가 다 짜야 하긴 하지만, 그만큼 컨트롤도 확실히 가능하다.
복잡한 조건이 필요한 쿼리나 성능 튜닝이 필요할 때 직접 건드릴 수 있다는 점이 꽤 큰 장점이다.</p>
<hr>
<h4 id="jpa는-repository-인터페이스만-만들어도-기본적인-crud는-다-된다"><strong>JPA는 Repository 인터페이스만 만들어도 기본적인 CRUD는 다 된다</strong></h4>
<p>Spring Data JPA에서 제공하는 JpaRepository를 상속받으면
별다른 설정 없이도 데이터를 저장하고 조회할 수 있다.</p>
<pre><code class="language-java">public interface UserRepository extends JpaRepository&lt;User, Long&gt; {
    Optional&lt;User&gt; findByUsername(String username);
}</code></pre>
<p>메서드 이름만 지어도 내부적으로 쿼리가 생성돼서 실행된다.
만약 복잡한 쿼리가 필요하다면 @Query 어노테이션을 써서 JPQL을 직접 작성할 수도 있다.</p>
<p>코드를 객체 중심으로 작성할 수 있다는 게 가장 큰 장점이고,
설계가 잘 되어 있다면 유지보수나 확장도 수월하게 할 수 있다.</p>
<hr>
<p>이렇게 동작 방식까지 살펴보니,
JPA는 편리함과 구조적인 설계를 지향하고,
MyBatis는 쿼리 제어의 유연함과 성능 중심의 설계에 가깝다는 게 확실히 보인다.</p>
<p>둘 다 장단점이 확실해서 프로젝트 상황에 따라 적절히 고르는 게 중요한 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[👨🏻‍💻String은 왜 대문자 일까?]]></title>
            <link>https://velog.io/@raymond_yoon/String%EC%9D%80-%EC%99%9C-%EB%8C%80%EB%AC%B8%EC%9E%90-%EC%9D%BC%EA%B9%8C</link>
            <guid>https://velog.io/@raymond_yoon/String%EC%9D%80-%EC%99%9C-%EB%8C%80%EB%AC%B8%EC%9E%90-%EC%9D%BC%EA%B9%8C</guid>
            <pubDate>Sat, 01 Mar 2025 09:44:14 GMT</pubDate>
            <description><![CDATA[<p>스프링 프로젝트를 진행하다 보면 다양한 데이터 타입을 다루게 되는데, 그중에서도 int는 소문자로 시작하는 반면 String은 대문자로 시작하는 점이 눈에 띄었다. 처음에는 단순한 문법 차이라고 생각했지만, 이를 이해하려면 Java의 기본 타입(Primitive Type)과 참조 타입(Reference Type)에 대한 개념을 알아야 했다.</p>
<hr>

<h2 id="기본-타입primitive-type과-참조-타입reference-type">기본 타입(Primitive Type)과 참조 타입(Reference Type)</h2>
<p>스프링에서 엔티티(Entity)를 만들 때, 필드 타입을 지정하는 과정에서 기본 타입과 참조 타입 중 어떤 것을 선택해야 할지 고민할 때가 있다. 이 둘의 차이를 알면 더 적절한 선택을 할 수 있다.</p>
<h3 id="기본-타입-primitive-type">기본 타입 (Primitive Type)</h3>
<p>기본 타입은 가장 단순한 데이터 타입으로, 메모리에서 직접 값을 저장하는 방식으로 동작한다. 대표적인 기본 타입은 다음과 같다.</p>
<ul>
<li>byte, short, int, long (정수형)</li>
<li>float, double (실수형)</li>
<li>char (문자형)</li>
<li>boolean (논리형)</li>
</ul>
<p>기본 타입은 고정된 크기의 메모리를 차지하며, 값을 직접 저장한다. 예를 들어 int a = 10;이라고 선언하면, a에는 실제 숫자 10이 저장된다.</p>
<h3 id="참조-타입-reference-type">참조 타입 (Reference Type)</h3>
<p>반면, String과 같은 참조 타입은 실제 값을 저장하는 것이 아니라, 객체의 주소(Reference)를 저장한다. 즉, String str = &quot;Hello&quot;; 라고 선언하면, &quot;Hello&quot;라는 문자열 객체가 메모리에 생성되고, str 변수는 그 객체의 주소를 가리킨다.</p>
<p>Java에서 String은 클래스이므로 대문자로 시작하며, 이는 객체 지향 프로그래밍에서 일반적인 네이밍 규칙이다. 반면 int와 같은 기본 타입은 더 가볍고 메모리를 효율적으로 관리하기 위해 소문자로 지정되어 있다.</p>
<hr>

<h2 id="스프링에서의-적용">스프링에서의 적용</h2>
<p>스프링 프로젝트에서 엔티티(Entity) 클래스의 필드를 정의할 때, 기본 타입과 참조 타입 중 어떤 것을 사용할지 고민할 수 있다. 예를 들어:</p>
<pre><code class="language-java">@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private int age; // 기본 타입
    private String name; // 참조 타입
}</code></pre>
<p>위 코드에서 age는 기본 타입 int를 사용했으며, name은 String 참조 타입을 사용했다. int와 같은 기본 타입은 null 값을 가질 수 없지만, Integer와 같은 래퍼 클래스(Wrapper Class)를 사용하면 null 처리가 가능하다.</p>
<pre><code class="language-java">private Integer age; // null 값 허용 가능</code></pre>
<h3 id="정리">정리</h3>
<table>
<thead>
<tr>
<th>타입</th>
<th align="left">종류</th>
<th align="center">시작 문자</th>
<th align="right">저장 방식</th>
</tr>
</thead>
<tbody><tr>
<td>int</td>
<td align="left">기본 타입</td>
<td align="center">소문자</td>
<td align="right">값을 직접 저장</td>
</tr>
<tr>
<td>String</td>
<td align="left">참조 타입(클래스)</td>
<td align="center">대문자</td>
<td align="right">객체의 주소를 저장</td>
</tr>
</tbody></table>
<p>스프링 프로젝트를 진행하면서 이런 차이를 이해하면 더 적절한 타입을 선택할 수 있다. 앞으로도 프로젝트를 하면서 궁금했던 점들을 정리해보려고 한다. 😊</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[🦁멋쟁이사자처럼 12기 중앙 해커톤]]></title>
            <link>https://velog.io/@raymond_yoon/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-12%EA%B8%B0-%EC%A4%91%EC%95%99%ED%95%B4%EC%BB%A4%ED%86%A4</link>
            <guid>https://velog.io/@raymond_yoon/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-12%EA%B8%B0-%EC%A4%91%EC%95%99%ED%95%B4%EC%BB%A4%ED%86%A4</guid>
            <pubDate>Sat, 01 Feb 2025 12:34:15 GMT</pubDate>
            <description><![CDATA[<p>매우 늦은 해커톤 후기와 나의 첫 백엔드로써 프로젝트에서 내 코드리뷰를 해볼 생각이다.
지금은 갓태어난 새 라면 이때는 알에 있었던 시기라고 할 수 있다. 그래도 운이 좋아서 이렇게 친구덕에 해커톤 경험도 해보고 너무 좋은 경험이였다. 성장 할 수 있는 곳에 가서 나의 실력을 매우 성장하고 싶다. 오히려 늦은 후기가 지금은 더 좋은 것 같다. 그 이유는 그때 보다 성장한 내가 그때 내가 했던 것을 보는건 좋은 경험이 될 것 같기 때문이다.</p>
<hr>

<h1 id="해커톤-후기">해커톤 후기</h1>
<p><img src="https://velog.velcdn.com/images/raymond_yoon/post/c2d2950d-e586-4e6c-8309-8c812157b6ce/image.jpg" alt=""></p>
<p>해커톤을 처음 가봤는데 사람이 진짜 진짜 많았다.. 다른 학교들 사람들도 보면서 내가 이런 경험도 해보다니 너무 좋은 경험이라고 생각했다. </p>
<p><img src="https://velog.velcdn.com/images/raymond_yoon/post/fb257253-9074-4d1e-9239-4659baf76165/image.jpg" alt=""></p>
<p>친구들이랑 이런 사진도 찍구 
<img src="https://velog.velcdn.com/images/raymond_yoon/post/78e0093b-6d44-4ea1-b182-3fb12c35c527/image.jpg" alt=""></p>
<p>날씨도 너무너무 좋았다~ 
여러가지 부스 체험도 해보고 팀원끼리 이야기도 나누면서 새벽을 보내고 좋은 경험이였다~</p>
<p>내가 백엔드로 가고 첫 프로젝트였는데 간단한 CRUD 구현과 네이버 뉴스 API 활용하는 담당을 맡았다. 
솔직히 지금도 모르겠지만, 저 때는 내가 할 수 있을까? 라는 생각이 들었던것 같다.
간단한 CRUD 구현조차 구글링과 gpt의 도움을 받고, 네이버 뉴스 API 활용하는것은 구글링과 네이버 공식 문서 및 gpt를 쓰는데도 시간이 많이 걸렸다.
하지만 이렇게 하면서 성장을 많이 한것 같다.</p>
<p>내가 맡은 부분 중 2가지를 봐볼건데 좋아요 기능과 네이버뉴스 API를 활용하는 것이다. </p>
<hr>
<h2 id="첫-해커톤-프로젝트-리뷰--좋아요-기능과-네이버-뉴스-api-활용기">첫 해커톤 프로젝트 리뷰 : 좋아요 기능과 네이버 뉴스 API 활용기</h2>
<h3 id="code-review--좋아요like-기능-구현-및-개선-포인트">Code Review : 좋아요(Like) 기능 구현 및 개선 포인트</h3>
<ul>
<li>프로젝트 구조<blockquote>
<p>like/<br>├── common/
│    └── ResponseResult.java<br>├── controller/
│    └── LikeController.java<br>├── dto/
│    └── LikeRequestDTO.java<br>├── entity/
│    └── Like.java<br>├── exception/ 
│    └── CustomException.java<br>├── repository/
│    └── LikeRepository.java<br>└── service/
ㅒ    └── LikeService.java  </p>
</blockquote>
</li>
</ul>
<p>프로젝트 구조는 각 계층별 역할을 분리하여 유지보수와 확장에 용이한 구조가 아닐까 생각한다! (GPT 최고!)
근데 나중에 프로젝트할때 같이 하는 사람마다 다르게 분류할 수 도 있다는 것을 깨달았다! </p>
<p><strong>&lt;각 파일 및 코드 리뷰&gt;</strong></p>
<p><strong>(common)</strong></p>
<ul>
<li><strong>ResponseResult.java</strong></li>
<li><em>역할:*</em> API 응답 구조(성공 여부, 데이터, 에러 메시지) 통일 관리</li>
<li><em>아쉬운 점:*</em> 응답에 타임스탬프 등 추가 메타 정보 부족</li>
</ul>
<p><strong>(controller)</strong></p>
<ul>
<li><strong>LikeController.java</strong></li>
<li><em>역할:*</em> 클라이언트의 좋아요 추가/삭제 요청을 받아 서비스 호출 및 결과 반환</li>
<li><em>아쉬운 점:*</em> 중복 try-catch로 인한 코드 반복, 글로벌 예외 처리 미적용</li>
</ul>
<p><strong>(dto)</strong></p>
<ul>
<li><strong>LikeRequestDTO.java</strong></li>
<li><em>역할:*</em> 좋아요 요청에 필요한 postId 데이터 캡슐화</li>
<li><em>아쉬운 점:*</em> 향후 확장 대비 추가 데이터 고려 부족</li>
</ul>
<p><strong>(entity)</strong></p>
<ul>
<li><strong>Like.java</strong></li>
<li><em>역할:*</em> 좋아요 정보를 DB에 매핑, Board와 Member와의 연관관계 관리</li>
<li><em>아쉬운 점:*</em> 양방향 연관관계 설정 미검토, 로깅 기능 부재</li>
</ul>
<p><strong>(exception)</strong></p>
<ul>
<li><strong>CustomException.java</strong></li>
<li><em>역할:*</em> 좋아요 기능 관련 예외 상황 전달</li>
<li><em>아쉬운 점:*</em> 에러 코드 등 상세 예외 정보 미포함</li>
</ul>
<p><strong>(repository)</strong></p>
<ul>
<li><strong>LikeRepository.java</strong></li>
<li><em>역할:*</em> DB에서 Like 관련 CRUD 및 존재 여부 확인</li>
<li><em>아쉬운 점:*</em> 복잡한 쿼리 최적화나 커스텀 쿼리 고려 미흡</li>
</ul>
<p><strong>(service)</strong></p>
<ul>
<li><strong>LikeService.java</strong></li>
<li><em>역할:*</em> 좋아요 생성/삭제, 중복 체크, 트랜잭션 관리 및 추천수 업데이트</li>
<li><em>아쉬운 점:*</em> boardService 의존성으로 인한 도메인 간 결합, 이벤트 기반 처리 미고려, 로그 기록 미흡</li>
</ul>
<h3 id="code-review--좋아요like-기능-구현-및-개선-포인트-1">Code Review : 좋아요(Like) 기능 구현 및 개선 포인트</h3>
<ul>
<li>프로젝트 구조<blockquote>
<p>healthnews/
├── client/<br>│    └── JsoupCrawling.java
├── controller/<br>│    └── HealthNewsController.java
├── dto/<br>│    ├── HealthNewsResponseDto.java
│    ├── NewsApiResponseDto.java
│    └── NewsSummaryDto.java
├── entity/<br>│    └── HealthNews.java 
├── exception/<br>│    ├── NaverApiCallException.java
│    ├── NewsControllerAdvice.java
│    └── NewsNullException.java 
├── repository/<br>│    ├── HealthNewsRepository.java
│    └── NewsApiResponseRepository.java
└── service/<br>ㅐ  └── HealthNewsService.java</p>
</blockquote>
</li>
</ul>
<p><strong>&lt;각 파일 및 코드 리뷰&gt;</strong></p>
<p><strong>(client)</strong></p>
<ul>
<li><strong>JsoupCrawling.java</strong></li>
<li><em>역할:*</em> Jsoup을 사용해 URL의 HTML을 파싱하고, 지정된 쿼리로 요소(Elements)를 추출하여 반환</li>
<li><em>아쉬운 점:*</em> 예외 발생 시 단순 e.printStackTrace()에 의존, 로깅 및 재시도 메커니즘 미구현</li>
</ul>
<p><strong>(controller)</strong></p>
<ul>
<li><strong>HealthNewsController.java</strong></li>
<li><em>역할:*</em> 키워드 기반 건강 뉴스 검색과 사용자 건강 상태 기반 뉴스 제공 API 엔드포인트 관리</li>
<li><em>아쉬운 점:*</em> 검색과 사용자 뉴스 조회 기능이 혼재, 입력 검증 및 예외 처리 보완 필요</li>
</ul>
<p><strong>(dto)</strong></p>
<ul>
<li><p><strong>HealthNewsResponseDto.java</strong></p>
</li>
<li><p><em>역할:*</em> 뉴스 응답 데이터(제목, 링크, 설명, 이미지 URL)를 캡슐화하고 이미지 링크 유효성 검사 제공</p>
</li>
<li><p><em>아쉬운 점:*</em> 추가적인 메타 정보나 확장 시 고려할 필드 부족</p>
</li>
<li><p><strong>NewsApiResponseDto.java</strong></p>
</li>
<li><p><em>역할:*</em> 뉴스 API 응답 데이터를 담고, Naver 뉴스만 필터링 및 HealthNewsResponseDto 변환 제공</p>
</li>
<li><p><em>아쉬운 점:*</em> JsoupCrawling 인스턴스를 직접 생성하여 DI(의존성 주입) 미적용, 테스트 용이성 저하</p>
</li>
<li><p><strong>NewsSummaryDto.java</strong></p>
</li>
<li><p><em>역할:*</em> 뉴스 요약 정보를 담고, 이미지 크롤링을 통해 HealthNewsResponseDto로 변환</p>
</li>
<li><p><em>아쉬운 점:*</em> JsoupCrawling 의존성을 외부 주입 대신 내부 처리하여 유연성 부족</p>
</li>
</ul>
<p><strong>(entiry)</strong></p>
<ul>
<li><strong>HealthNews.java</strong></li>
<li><em>역할:*</em> 건강 뉴스 엔티티로, DB 테이블과 매핑되어 뉴스 저장 관리</li>
<li><em>아쉬운 점:*</em> 기본 CRUD 외에 추가 검색이나 비즈니스 로직 반영 미흡</li>
</ul>
<p><strong>(exception)</strong></p>
<ul>
<li><p><strong>NaverApiCallException.java</strong></p>
</li>
<li><p><em>역할:*</em> 네이버 API 호출 실패 시 발생하는 커스텀 예외 정의</p>
</li>
<li><p><em>아쉬운 점:*</em> 디버깅을 위한 추가 정보 제공 미흡</p>
</li>
<li><p><strong>NewsControllerAdvice.java</strong></p>
</li>
<li><p><em>역할:*</em> NewsNullException과 NaverApiCallException에 대한 전역 예외 처리</p>
</li>
<li><p><em>아쉬운 점:*</em> 추가 예외 유형 처리 및 통일된 응답 포맷 적용 필요</p>
</li>
<li><p><strong>NewsNullException.java</strong></p>
</li>
<li><p><em>역할:*</em> 뉴스 결과가 없을 경우 발생하는 커스텀 예외 정의</p>
</li>
<li><p><em>아쉬운 점:*</em> 상세 메시지 및 예외 코드 부여 미흡</p>
</li>
</ul>
<p><strong>(repository)</strong></p>
<ul>
<li><p><strong>HealthNewsRepository.java</strong></p>
</li>
<li><p><em>역할:*</em> HealthNews 엔티티에 대한 CRUD 작업을 수행하는 Repository</p>
</li>
<li><p><em>아쉬운 점:*</em> 기본 CRUD 외 추가 검색 조건 및 페이징 기능 미구현</p>
</li>
<li><p><strong>NewsApiResponseRepository.java</strong></p>
</li>
<li><p><em>역할:*</em> 네이버 뉴스 API 호출 및 응답 데이터를 Optional 형태로 반환</p>
</li>
<li><p><em>아쉬운 점:*</em> RestTemplate 사용 시 커넥션 타임아웃, 에러 핸들링 개선 필요</p>
</li>
</ul>
<p><strong>(service)</strong></p>
<ul>
<li><strong>HealthNewsService.java</strong></li>
<li><em>역할:*</em> 네이버 뉴스 API를 통해 뉴스 데이터를 수집, 필터링, 가공하여 최종 HealthNewsResponseDto로 반환 및 DB저장</li>
<li><em>아쉬운 점:*</em> 반복 API 호출 시 매직넘버(2000, 100, 4)사용, API 제한 고려 및 효율성 개선 필요</li>
</ul>
<hr>

<h2 id="마무리-한마디"><strong>마무리 한마디</strong></h2>
<p>이번 리뷰를 통해서 좋아요 기능과 네이버 뉴스 API 모두에서 전역 예외 처리, 로깅, 의존성 관리 및 API 호출 최적화 등 개선할 부분이 많음을 확인했으니 앞으로 이러한 점들을 보완해 더 안정적이고 확장 가능한 시스템을 만들어보자잇~ 나의 코드를 보는 눈을 더 키워 봅시다! </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[👨🏻‍💻자료구조란 무엇일까?]]></title>
            <link>https://velog.io/@raymond_yoon/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C</link>
            <guid>https://velog.io/@raymond_yoon/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C</guid>
            <pubDate>Tue, 05 Nov 2024 06:43:56 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/raymond_yoon/post/aac432cf-7ecb-4468-9696-92ede575ace5/image.png" alt=""></p>
<p>취업을 하여 실무를 경험하고 싶은 요즘 스펙도 없고 요즘 자존감이 떨어지고 불안한 시기인 아무것도 아닌 나를 위해 좀 더 성실하고 열심히 살기위해 글을 쓴다. 스티브 잡스분께서 말씀하신 <strong>&quot;어제를 뒤돌아보는 건 그만하자. 그 대신 내일을 발전시켜 나가자.&quot;</strong> 라는 말을 생각하며 나는 더 나은 내일을 위해 발전시켜 나가는것이다! 취업하고싶다아</p>
<hr>
<hr>

<h1 id="자료구조란-무엇">자료구조란 무엇?</h1>
<blockquote>
<p>프로그래밍에서 효율적인 데이터 관리와 처리를 위해 필수적으로 알아야 하는 개념이 바로 자료구조라고 생각한다.
자료구조는 데이터를 저장하고 조직화하는 방법을 의미하고 알고리즘과 결합하여 프로그램의 성능을 최적화할 수 있다.
이 글에서는 자료구조의 기본 개념과 몇 가지 주요 자료구조를 살펴볼 것이다.</p>
</blockquote>
<hr>

<h2 id="1-자료구조의-필요성">1. 자료구조의 필요성</h2>
<p>자료구조는 다양한 형태의 데이터를 적절히 조직화하여 더 효율적인 연산을 가능하게 한다. 예를 들어 특정 데이터를 빠르게 검색해야 할 때 적합한 자료구조를 사용하면 검색 속도를 크게 향상시킬 수 있다.
효율적인 자료구조를 선택함으로써 시간과 메모리를 절약하고 프로그램의 성능을 최적화할 수 있다.</p>
<hr>

<h2 id="2-자료구조의-종류">2. 자료구조의 종류</h2>
<h3 id="2-1-배열-array">2-1 배열 (Array)</h3>
<p>배열은 같은 데이터 타입의 요소들을 연속된 메모리 공간에 저장하는 자료구조이다. 각 요소는 고유한 인덱스를 통해 접근할 수 있다.
<img src="https://velog.velcdn.com/images/raymond_yoon/post/4e0d988c-cfad-49a1-a75d-bcdd37942e90/image.png" alt=""></p>
<h4 id="장점">장점</h4>
<ul>
<li><strong>빠른 접근 속도:</strong> 인덱스를 통해 O(1) 시간 복잡도로 빠르게 특정 요소에 접근할 수 있다.</li>
<li><strong>간단한 구조:</strong> 메모리 구조가 단순하고 이해하기 쉬워 자주 사용된다.</li>
</ul>
<h4 id="단점">단점</h4>
<ul>
<li><strong>고정된 크기:</strong> 선언 시 크기를 지정해야 하므로 크기 변경이 어렵다.</li>
<li><strong>비효율적인 삽입/삭제:</strong> 중간에 데이터를 삽입하거나 삭제할 경우에 데이터를 이동해야 하므로 시간 복잡도가 O(n)이다.<hr>

</li>
</ul>
<h3 id="2-2-연결-리스트-linked-list">2-2 연결 리스트 (Linked List)</h3>
<p>연결 리스트는 각각의 노드가 데이터와 다음 노드를 가리키는 포인터를 포함하는 구조이다. 노드를 동적으로 추가 및 삭제할 수 있어 유연성이 높다.
<img src="https://velog.velcdn.com/images/raymond_yoon/post/89c36a78-e5ad-4b06-a93b-1ee820bc390a/image.png" alt=""></p>
<h4 id="장점-1">장점</h4>
<ul>
<li><strong>동적 크기 조절:</strong> 필요에 따라 크기를 유연하게 변경할 수 있다.</li>
<li><strong>효율적인 삽입/삭제:</strong> 중간 삽입 및 삭제 시 데이터를 이동할 필요가 없어 O(1) 시간 복잡도로 처리할 수 있다.</li>
</ul>
<h4 id="단점-1">단점</h4>
<ul>
<li><strong>느린 접근 속도:</strong> 배열과 달리 인덱스로 접근할 수 없기 때문에 특정 노드에 접근하려면 처음부터 순회해야 한다.</li>
<li><strong>추가 메모리 필요:</strong> 각 노드마다 다음 노드를 가리키는 포인터를 저장해야 하므로 메모리 사용량이 증가한다.<hr>

</li>
</ul>
<h3 id="2-3-스택-stack">2-3 스택 (Stack)</h3>
<p>스택은 데이터를 한쪽 끝에서만 삽입하고 제거하는 자료구조로, LIFO(Last In, First Out) 원칙을 따른다. 대표적으로 함수 호출, 되돌리기 기능 등에 사용된다.
<img src="https://velog.velcdn.com/images/raymond_yoon/post/05447d4f-8fd5-4698-8cfb-43a41b06da41/image.png" alt=""></p>
<h4 id="장점-2">장점</h4>
<ul>
<li><strong>간단한 구현:</strong> 삽입 및 삭제가 한쪽 끝에서만 이렁나기 때문에 구조가 단순하다.</li>
<li><strong>빠른 삽입/삭제:</strong> O(1) 시간 복잡도로 삽입과 삭제를 할 수 있다.</li>
</ul>
<h4 id="단점-2">단점</h4>
<ul>
<li><strong>제한된 접근 방식:</strong> 마지막에 삽입된 요소만 접근 가능하여 중간 요소를 접근할 수 없다.<hr>

</li>
</ul>
<h3 id="2-4-큐-queue">2-4 큐 (Queue)</h3>
<p>큐는 FIFO(First In, First Out) 원칙을 따르는 자료구조이다. 데이터를 앞쪽에서 제거하고 뒤쪽에 삽입한다. 대기열과 같은 구조에 적합하다.
<img src="https://velog.velcdn.com/images/raymond_yoon/post/0e110f45-c6d3-4d9e-a919-14bc50625fb2/image.png" alt=""></p>
<h4 id="장점-3">장점</h4>
<ul>
<li><strong>순서 유지:</strong> 먼저 들어온 데이터가 먼저 나가는 구조로, 순서가 중요한 작업에 유리하다.</li>
<li><strong>빠른 삽입/삭제:</strong> 삽입과 삭제가 양쪽 끝에서만 일어나므로 O(1) 시간 복잡도로 효율적이다.</li>
</ul>
<h4 id="단점-3">단점</h4>
<ul>
<li><strong>제한된 접근 방식:</strong> 중간에 있는 요소에 직접 접근할 수 없다.<hr>

</li>
</ul>
<h3 id="2-5-트리-tree">2-5 트리 (Tree)</h3>
<p>트리는 계층 구조를 가지는 비선형 자료구조로, 노드 간 부모-자식 관계가 존재한다. 이진 트리, AVL 트리, B-트리 등 다양한 형태가 있으며 데이터 구조와 알고리즘 문제에서 자주 사용된다.
<img src="https://velog.velcdn.com/images/raymond_yoon/post/008ef6a0-dd50-41eb-91bd-759aa44fa2fe/image.png" alt=""></p>
<h4 id="장점-4">장점</h4>
<ul>
<li><strong>빠른 탐색:</strong> 정렬된 트리 구조에서는 원하는 데이터를 빠르게 탐색할 수 있다. 예를 들면 이진 탐색 트리는 O(log n) 시간 복잡도를 가진다.</li>
<li><strong>계층적 데이터 표현:</strong> 트리 구조를 통해 계층적으로 데이터를 나타낼 수 있어 파일 시스템이나 조직도 표현에 적합하다.</li>
</ul>
<h4 id="단점-4">단점</h4>
<ul>
<li><strong>복잡한 구현:</strong> 삽입, 삭제, 균형 조정이 복잡할 수 있고 메모리와 연산 성능이 요구된다.</li>
<li><strong>비용이 많이 드는 균형 유지:</strong> 자가 균형 트리(BST)의 경우 삽입과 삭제 시 균형을 맞추기 위해 추가 연산이 필요하다.<hr>

</li>
</ul>
<h3 id="2-6-그래프-graph">2-6 그래프 (Graph)</h3>
<p>그래프는 노드(정점)와 그 노드들을 연결하는 간선(엣지)으로 이루어진 자료구조이다. 방향 그래프, 무방향 그래프, 가중치 그래프 등 다양한 종류가 있고 네트워크, 경로찾기 등의 문제에 활용된다.
<img src="https://velog.velcdn.com/images/raymond_yoon/post/8b724feb-fc6e-425c-af4c-430e4edfcccb/image.png" alt=""></p>
<h4 id="장점-5">장점</h4>
<ul>
<li><strong>복잡한 관계 표현 가능:</strong> 그래프는 네트워크, 지도 등 복잡한 데이터 간의 관계를 표현하는데 유리하다.</li>
<li><strong>유연한 구조:</strong> 장점과 간선의 관계를 자유롭게 설정할 수 있어 다양한 문제를 해결할 수 있다.</li>
</ul>
<h4 id="단점-5">단점</h4>
<ul>
<li><strong>높은 메모리 사용량:</strong> 노드와 간선을 모두 저장해야 하므로 메모리 소모가 크다.</li>
<li><strong>복잡한 구현:</strong> 경로 탐색, 최단 거리 등 다양한 알고리즘이 필요해 구현이 복잡할 수 있다.</li>
</ul>
<hr>

<p>이처럼 각각의 자료구조는 특정한 장점과 단점을 가지고 있으므로 용도에 따라 최적의 자료구조를 선택하는 것이 중요하다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[🦁멋쟁이사자처럼 백엔드 스터디 (4)]]></title>
            <link>https://velog.io/@raymond_yoon/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-%EB%B0%B1%EC%97%94%EB%93%9C-%EC%8A%A4%ED%84%B0%EB%94%94-4</link>
            <guid>https://velog.io/@raymond_yoon/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-%EB%B0%B1%EC%97%94%EB%93%9C-%EC%8A%A4%ED%84%B0%EB%94%94-4</guid>
            <pubDate>Sun, 29 Sep 2024 03:37:40 GMT</pubDate>
            <description><![CDATA[<p>어느덧 마지막 학기를 시작한지 거의 한달이 되어간다. 추석도 있고 가족여행도 갔다와서 금방 지나간것 같다. 졸업을 해야되어서 지금 준비중인 논문 프로젝트와 졸업시험, 자격증, 취업 같은 걱정들이 너무 많다. 지금 진행중인 프로젝트도 잘됐음 좋겠고 모든게 다 잘되길 그리고 취업을 할 수 있었음 좋겠다. 그리고 내 주위 모두가 건강했으면 좋겠다.</p>
<h1 id="백엔드-스터디-4">백엔드 스터디 (4)</h1>
<hr>
이번 포스팅에서는 Spring Boot에서 자주 사용되는 주요 어노테이션과 REST API에서 자주 활용되는 컨트롤러 어노테이션을 정리했다. 기본 키(PK) 설정부터, 필드와 객체 매핑, 그리고 각 HTTP 메소드에 대응하는 컨트롤러 어노테이션을 설명한다.
<hr>

<h2 id="목차">목차</h2>
<ol>
<li>기본 키(PK) 어노테이션</li>
<li>필드/컬럼 어노테이션</li>
<li>객체와 테이블 매핑</li>
<li>REST API 개념 </li>
<li>컨트롤러에서 사용하는 어노테이션</li>
</ol>
<hr>

<h3 id="1-기본-키pk-어노테이션">1. 기본 키(PK) 어노테이션</h3>
<ul>
<li>@Id: 엔티티의 기본 키를 선언하는 어노테이션</li>
<li>@GeneratedValue: 기본 키 값을 자동으로 생성하는 전략을 설정하는 어노테이션<ul>
<li>GenerationType.AUTO : 자동으로 키를 생성</li>
<li>GenerationType.IDENTITY : 데이터베이스에 의해 키를 생성<pre><code class="language-java">@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;</code></pre>
</li>
</ul>
</li>
</ul>
<h3 id="2-필드컬럼-어노테이션">2. 필드/컬럼 어노테이션</h3>
<ul>
<li>@Column : 필드를 테이블 컬럼에 매핑할 때 사용되는 어노테이션<ul>
<li>name : 테이블의 컬럼명</li>
<li>nullable : null 값을 허용할지 여부</li>
<li>length : 문자열의 최대 길이 설정</li>
</ul>
</li>
</ul>
<pre><code class="language-java">@Column(name = &quot;username&quot;, nullable = false, length = 50)
private String name;
</code></pre>
<ul>
<li>@Enumerated : 자바의 enum 타입을 데이터베이스에 매핑할 때 사용<pre><code class="language-java">@Enumerated(EnumType.STRING)
private RoleType roleType;
</code></pre>
</li>
</ul>
<pre><code>
### 3. 객체와 테이블 매핑
Hibernate의 hbm2ddl.auto 설정을 통해 엔티티와 테이블 간의 매핑 및 자동 생성 여부를 설정할 수 있다.
* create, update, validate 등 다양한 옵션을 선택할 수 있으며, 실제 개발 환경에서 주의가 필요하다.

&lt;hr&gt;

### 4. REST API 개념
REST API는 웹 시스템에서 분산된 하이퍼미디어 시스템을 위한 소프트웨어 아키텍쳐이다. HTTP 프로토콜을 사용해 다양한 리소스를 주고받으며 각 리소스는 URL로 식별된다.

### 5. 컨트롤러에서 사용하는 어노테이션

@Controller
* @Controller와 @ResponseBody를 결합한 어노테이션으로 JSON/XML 형식의 데이터를 반환할때 사용된다.
```java
@RestController
public class MyRestController {
    @GetMapping(&quot;/greeting&quot;)
    public String greeting() {
        return &quot;Hello, World!&quot;;
    }
}</code></pre><p>@RequestParam</p>
<ul>
<li>URL 쿼리 파라미터로 전달된 값을 메소드 매개변수에 바인딩한다.<pre><code class="language-java">@GetMapping(&quot;/test&quot;)
public void test(@RequestParam String name, @RequestParam int price) {
  System.out.println(name);  // 출력: spring
  System.out.println(price); // 출력: 20000
}</code></pre>
</li>
</ul>
<p>@RequestBody</p>
<ul>
<li>HTTP 요청의 바디 부분을 자바 객체로 변환해준다. 주로 POST 메소드에서 사용된다.<pre><code class="language-java">@PostMapping(&quot;/new&quot;)
public void createQuestion(@RequestBody QuestionDTO dto) {
  // 처리 로직
}</code></pre>
</li>
</ul>
<p>@PathVariable</p>
<ul>
<li>URL 경로에서 값을 가져올때 사용된다. 경로에 있는 변수를 메소드 매개변수에 바인딩한다.<pre><code class="language-java">@GetMapping(&quot;/champions/{name}&quot;)
public List&lt;String&gt; getChampionByName(@PathVariable String name) {
  return championService.getChampionByName(name);
}</code></pre>
</li>
</ul>
<hr>

<h3 id="마무리">마무리</h3>
<p>Spring Boot에서 제공하는 어노테이션들은 REST API 개발에서 매우 유용하게 활용된다. 
이번 포스팅에서는 그중에서 자주 사용되는 기본 어노테이션들과 컨트롤러 어노테이션을 알아보았다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[🦁멋쟁이사자처럼 백엔드 스터디 (3)]]></title>
            <link>https://velog.io/@raymond_yoon/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-%EB%B0%B1%EC%97%94%EB%93%9C-%EC%8A%A4%ED%84%B0%EB%94%94-3</link>
            <guid>https://velog.io/@raymond_yoon/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-%EB%B0%B1%EC%97%94%EB%93%9C-%EC%8A%A4%ED%84%B0%EB%94%94-3</guid>
            <pubDate>Wed, 04 Sep 2024 11:38:25 GMT</pubDate>
            <description><![CDATA[<p>정보처리기사 필기도 보고, 해커톤도 하고, 운동도 하면서 방학을 보냈다. 뭔가 좀 더 바쁘게 살았어야했다는 생각이 들었지만, 내 인생에서 학생때의 마지막 방학이여서 뭔가 이제 정말 시작이구나 라고 생각했다. 항상 걱정은 많은 것 같다. 첫번째로 괜찮은 기업에 들어가서 많이 배우고 성장하고 싶다는 생각에 나를 좀 더 성장시켜야겠다고 생각했다. 내 학생 인생의 마지막 학기가 시작된다. 졸업요건도 생각하고 물론 배울수있는 좋은 회사에 들어가서 취업계 내서 졸업하면 최고Best라고 생각한다. 마지막 4학년 2학기가 끝나고 내가 어떻게 됐을지 궁금하면서 걱정된다. 잘 됐음 좋겠네. </p>
<h1 id="백엔드-스터디-3">백엔드 스터디 (3)</h1>
<hr>
이번 포스팅에서는 Spring에서 JPQL(Java Persistence Query Language)과 Spring Data JPA를 활용하여 데이터베이스와 상호작용하는 방법을 스터디한 내용을 공유하고자 한다. JPQL은 SQL과 유사한 문법을 가지면서도 엔티티 객체를 대상으로 쿼리를 작성할 수 있어 객체지향적인 프로그래밍에 적합하다. 또한 Spring Data JPA는 간단한 쿼리에서부터 복잡한 데이터 접근까지 편리하게 처리할 수 있는 기능을 제공한다. 
<hr>

<h2 id="구조">구조</h2>
<h3 id="애플리케이션-구조">애플리케이션 구조</h3>
<p>Spring 애플리케이션은 다음과 같은 주요 구성들로 이루어져 있다. 여기서 각 구성 요소가 어떻게 상호작용하는지 이해하는것이 중요하다.</p>
<ul>
<li><p>Controller : 클라이언트로부터 HTTP 요청을 받아 처리한다.</p>
</li>
<li><p>Service : 비즈니스 로직을 구현하며, 데이터를 처리하고 저장소와 상호작용한다.</p>
</li>
<li><p>Repository : 실제 데이터베이스와 상호작용하며, CRUD 작업을 처리한다.</p>
</li>
<li><p>DTO : 데이터 전송 객체로, 계층 간 데이터를 전달하는 역할을 한다.</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/raymond_yoon/post/9d274563-884e-44b5-b5e5-776210d60f1c/image.png" alt=""></p>
<h3 id="패키지-구조">패키지 구조</h3>
<p>다음은 일반적인 Spring 프로젝트의 패키지 구조다.</p>
<ul>
<li><p>config : 애플리케이션 설정 파일이 위치하는 패키지 </p>
</li>
<li><p>controller : HTTP 요청을 처리하는 컨트롤러 클래스가 위치</p>
</li>
<li><p>domain : 도메인 객체들이 정의된 곳 </p>
</li>
<li><p>repository : 데이터베이스와의 상호작용을 담당하는 리포지토리 클래스가 위치</p>
</li>
<li><p>service : 비즈니스 로직을 담당하는 서비스 클래스가 위치</p>
</li>
</ul>
<hr>

<h2 id="jpql-java-persistence-query-language">JPQL (Java Persistence Query Language)</h2>
<p>JPQL은 객체를 대상으로 쿼리를 작성하는 Java Persistence API의 쿼리 언어이다. SQL과 문법이 비슷하지만 테이블이 아닌 엔티티 객체에 대한 쿼리를 수행한다.</p>
<h3 id="jpql-기본-예시">JPQL 기본 예시</h3>
<pre><code class="language-java">@PersistenceContext
EntityManager em;

public void main save(Board board) {
    em.persist(board);
}</code></pre>
<p>EntityManager를 통해 Board 엔티티를 데이터베이스에 저장하는 방법을 보여준다.
JPQL을 사용하면 SQL과 유사하게 엔티티 객체를 조회하고 수정할 수 있다.</p>
<h3 id="jpql로-데이터-조회">JPQL로 데이터 조회</h3>
<p>JPQL을 사용해 데이터를 조회할 때는 @Query 어노테이션을 사용할 수 있다.</p>
<pre><code class="language-java">@Query(&quot;SELECT c FROM ChatRoom c WHERE c.roomId = :roomId&quot;)
ChatRoom findChatRoomByRoomId(@Param(&quot;roomId&quot;) String roomId);</code></pre>
<p>위 코드는 ChatRoom 엔티티에서 특정 roomId 값을 기준으로 데이터를 조회하는 JPQL 쿼리이다.</p>
<hr>

<h2 id="연결">연결</h2>
<p>Spring Data JPA와 데이터베이스를 연결하기 위해서는 설정 파일을 사용해 데이터베이스 관련 정보를 설정해야한다. </p>
<pre><code>dependencies {
    implementation&#39;org.mariadb.jdbc:mariadb-java-client:3.1.2&#39;
    implementation&#39;org.springframework.boot:spring-boot-starter-data-jpa&#39;
}
-----------------------------------------------
spring:
  datasource:
    driver-class-name: org.mariadb.jdbc.Driver
    url: jdbc:mariadb://localhost:8080/yourdb
    username: 사용자이름
    password: 비밀번호</code></pre><p>Spring Boot 애플리케이션이 MariaDB와 연결되도록 설정한다.</p>
<hr>

<h2 id="jpa-실습">JPA 실습</h2>
<h3 id="엔티티-클래스-정의">엔티티 클래스 정의</h3>
<pre><code class="language-java">@Entity
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class Gyumin {
    @Id
    @Column(name = &quot;Mccc_id&quot;)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String roomId;
    private String fileName;
    private String code;

    // 생성자
    public Gyumin(String roomId, String fileName, String code) {
        this.roomId = roomId;
        this.fileName = fileName;
        this.code = code;
    }
}
</code></pre>
<p>위 클래스는 Gyumin 이라는 엔티티로 데이터베이스 테이블의 컬럼과 매핑된다. 각 필드는 id, roomId, fileName, code와 같이 정의되었고 @Id로 기본 키가 지정되었다.</p>
<h3 id="repository-인터페이스">Repository 인터페이스</h3>
<p>JPA에서 데이터를 조회할 때 Repository 패턴을 사용한다. SnapshotRespository 인터페이스는 MongoDB와 JPA를 함께 사용하여 데이터를 다루는 방법을 보여준다.</p>
<pre><code class="language-java">public interface SnapshotRepository extends MongoRepository&lt;Snapshot, String&gt; {
    Snapshot findByRoomId(String roomId);
    List&lt;Snapshot&gt; findSnapshotsByRoomId(String roomId);
    Snapshot findByRoomIdAndFileName(String roomId, String fileName);
}</code></pre>
<p>위 코드는 SnapshotRepository를 통홰 roomId로 데이터를 조회하는 쿼리를 작성하는 방법을 보여준다. findByRoomId는 roomId 값으로 데이터를 조회하는 기능을 수행하며 findSnapshotsByRoomId는 동일한 roomId로 여러 개의 Snapshot 리스트를 반환한다.</p>
<h3 id="jpql을-사용한-쿼리-작성">JPQL을 사용한 쿼리 작성</h3>
<p>JPA에서는 @Query 어노테이션을 사용하여 JPQL 쿼리를 정의할 수 있다.</p>
<pre><code class="language-java">public interface ManageRepository extends JpaRepository&lt;Manage, Long&gt; {

    @Query(&quot;select m from Manage m &quot; +
           &quot;where m.team.id = :teamId &quot; +
           &quot;order by m.createDate&quot;)
    List&lt;Manage&gt; findManagesByTeamId(@Param(&quot;teamId&quot;) Long teamId);
}</code></pre>
<p>위 코드에서는 @Query 어노테이션을 통해 직접 JPQL 쿼리를 작성하였고, teamId 파라미터를 사용하여 team.id에 해당하는 데이터를 조회한다. @Param 어노테이션을 사용하여 쿼리 매개변수를 바인딩한다.</p>
<hr>

<h2 id="벌크성-쿼리-builk-queries">벌크성 쿼리 (Builk Queries)</h2>
<p>벌크성 쿼리는 다수의 데이터를 한번에 처리하는데 사용된다. 대용량 데이터를 업데이트하거나 삭제할 때 성능 최적화에 유리하다.</p>
<h3 id="벌크-업데이트-예시">벌크 업데이트 예시</h3>
<pre><code class="language-java">@Modifying
@Query(&quot;UPDATE Member m SET m.age = m.age + 1 WHERE m.age &gt;= :age&quot;)
int bulkUpdateAge(@Param(&quot;age&quot;) int age);</code></pre>
<p>위 쿼리는 age 값이 일정 수준 이상인 회원들의 나이를 일괄적으로 1씩 증가시키는 벌크 업데이트 쿼리이다. @Modifying 어노테이션을 사용하여 업데이트 쿼리를 처리할 수 있다.</p>
<hr>
이번에는 Spring에서 JPQL과 JPA를 사용하여 객체지향적으로 데이터를 처리하는 방법을 알았다. JPQL을 통해 복잡한 쿼리도 쉽게 작성할 수 있으며 벌크성 쿼리를 통해 대량 데이터를 효율적으로 처리할 수 있다. 앞으로 더 복잡한 JPQL 쿼리 작성법과 성능 최적화 방법을 더 알아보자 ]]></description>
        </item>
        <item>
            <title><![CDATA[🦁멋쟁이사자처럼 백엔드 스터디 (2)]]></title>
            <link>https://velog.io/@raymond_yoon/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-%EB%B0%B1%EC%97%94%EB%93%9C-%EC%8A%A4%ED%84%B0%EB%94%94-2</link>
            <guid>https://velog.io/@raymond_yoon/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-%EB%B0%B1%EC%97%94%EB%93%9C-%EC%8A%A4%ED%84%B0%EB%94%94-2</guid>
            <pubDate>Wed, 14 Aug 2024 07:35:07 GMT</pubDate>
            <description><![CDATA[<p>스터디를 한지 시간이 오래 지나긴 했지만, 기록이 진짜 중요하고, 이런 기록 하나하나가 나에게 도움 된다고 생각해서 나를 위해 쓴다고 생각하여 늦게라도 쓴다.</p>
<p>지금은 멋쟁이사자처럼 대학 12th HACKATHON을 경험하고 온 후여서 그때 스터디할때는 이게 어떻게 쓰이지? 뭐지? 라는 생각이 해커톤을 경험한 후, 아 이렇게 쓰이는구나 이런식이구나 라는걸 많이 깨달았다. 
역시 나는 몸으로 직접 부딪치면서 깨달아야한다는것을 알았다. 참 많은 도움이 된 해커톤이여서 빠른 시일 내에 회고를 해야겠다는 생각이 들었다. </p>
<h1 id="백엔드-스터디-2">백엔드 스터디 (2)</h1>
<hr>

<h2 id="용어">용어</h2>
<h3 id="연관관계-association">연관관계 (Association)</h3>
<p>엔티티 간의 관계를 의미하며, 데이터베이스의 테이블 간 외래 키 관계에 해당한다.</p>
<h3 id="매핑-mapping">매핑 (Mapping)</h3>
<p>객체 모델과 데이터베이스 모델 간의 관계를 정의하는 작업. JPA에서는 엔티티와 테이블을 매핑하고, 연관관계 매핑에서는 엔티티 간의 관계를 정의한다.</p>
<h3 id="단방향-연관관계-unidirectional-association">단방향 연관관계 (Unidirectional Association)</h3>
<p>한 쪽 엔티티만 다른 엔티티를 참조하는 관계</p>
<h3 id="양방향-연관관계-bidirectional-association">양방향 연관관계 (Bidirectional Association)</h3>
<p>두 엔티티가 서로를 참조하는 관계</p>
<h3 id="일대일-one-to-one">일대일 (One-to-One)</h3>
<p>한 엔티티가 하나의 다른 엔티티와 연관된 관계</p>
<h3 id="일대다-one-to-many">일대다 (One-to-Many)</h3>
<p>한 엔티티가 여러 엔티티와 연관된 관계</p>
<h3 id="다대일-many-to-one">다대일 (Many-to-One)</h3>
<p>여러 엔티티가 한 엔티티와 연관된 관계</p>
<h3 id="다대다-many-to-many">다대다 (Many-to-Many)</h3>
<p>여러 엔티티가 여러 엔티티와 연관된 관계</p>
<h3 id="주인-엔티티-owner-entity">주인 엔티티 (Owner Entity)</h3>
<p>연관관계에서 외래키를 소유하고 있는 엔티티</p>
<h3 id="주인-아닌-엔티티-non-owner-entity">주인 아닌 엔티티 (Non-Owner Entity)</h3>
<p>외래키를 소유하고 있지 않은 엔티티</p>
<hr>


<h2 id="연관관계의-필요성">연관관계의 필요성</h2>
<blockquote>
<p>객체 모델에서는 실제 비즈니스 로직을 구현하기 위해 엔티티간의 관계를 명확히 정의해야 한다. 데이터베이스의 테이블 간 외래키와 같은 관계를 객체 지향적으로 다루기 위해 연관관계가 필요하다.</p>
</blockquote>
<h2 id="매핑의-목적">매핑의 목적</h2>
<blockquote>
<p>객체 지향 코드와 관계형 데이터베이스의 구조를 일치시켜 데이터의 일관성을 유지하기 위함이다.
이를 통해 객체 모델을 통해 자연스럽게 데이터베이스 상호작용을 구현할 수 있다.</p>
</blockquote>
<hr>

<p><img src="https://velog.velcdn.com/images/raymond_yoon/post/825f288d-a80f-4bae-8386-8e8dbbe7c209/image.png" alt=""></p>
<p>객체 연관관계는 간접적이고, 테이블 연관관계는 외래 키로 명확히 표현되며, JPA 매핑을 통해 이를 일관되게 연결할 수 있다.</p>
<hr>

<h2 id="연관관계-매핑의-종류">연관관계 매핑의 종류</h2>
<h3 id="일대일one-to-one-관계">일대일(One-to-One) 관계</h3>
<p><img src="https://velog.velcdn.com/images/raymond_yoon/post/27e66a9d-bf98-4eb3-96d1-8abed478b0f8/image.png" alt=""></p>
<h4 id="객체-연관관계">객체 연관관계</h4>
<p>Member 객체와 Locker 객체가 1:1로 매핑되어 있다. 
즉, Member는 하나의 Locker를 가질 수 있고, Locker도 하나의 Member에만 연결된다.</p>
<h4 id="테이블-연관관계">테이블 연관관계</h4>
<p>데이터베이스에서는 MEMBER 테이블이 LOCKER_ID를 외래 키로 가지며, 이 키를 통해 LOCKER 테이블과 1:1 관계를 맺는다.
LOCKER_ID 필드는 외래 키(FK)이면서 유니크(UNIQUE) 제약조건이 설정되어 있어, 각 Locker는 하나의 Member와만 연관될 수 있다.</p>
<blockquote>
<p>일대일 관계는 두 엔티티가 서로 1:1로 매핑되는 관계로, 하나의 객체가 다른 하나의 객체에만 연관되는 구조이다. 
데이터베이스에서는 외래 키와 유니크 제약조건을 사용해 이를 구현하며, 객체 모델에서는 필드 참조를 통해 연관관계를 표현한다.</p>
</blockquote>
<h3 id="다대일many-to-one-관계">다대일(Many-to-One) 관계</h3>
<h4 id="단방향">&lt;단방향&gt;</h4>
<p><img src="https://velog.velcdn.com/images/raymond_yoon/post/dbb9abb9-2a55-4ae4-aa12-e07902b838aa/image.png" alt=""></p>
<h4 id="객체-연관관계-1">객체 연관관계</h4>
<p>Member 객체와 Team 객체가 다대일 관계를 가진다. 
즉, 여러 Member 객체가 하나의 Team 객체와 연관될 수 있다.
Member 클래스는 Team team 필드를 통해 Team 객체를 참조한다. 
이는 여러 Member가 동일한 Team 객체에 속할 수 있음을 나타낸다.</p>
<h4 id="테이블-연관관계-1">테이블 연관관계</h4>
<p>데이터베이스에서는 MEMBER 테이블이 TEAM_ID를 외래 키(FK)로 가지며, 이를 통해 TEAM 테이블과의 관계를 정의한다.
TEAM_ID 필드는 MEMBER 테이블에 속하며, 이는 여러 MEMBER가 동일한 TEAM에 연결될 수 있도록 한다.</p>
<blockquote>
<p>다대일 관계는 여러 엔티티가 하나의 엔티티와 연관되는 관계로, 객체 모델에서는 Member가 Team을 참조하는 방식으로 표현된다. 
데이터베이스에서는 외래 키를 통해 이 관계를 구현하며, 여러 Member가 같은 Team에 속할 수 있다. 이 구조는 팀과 소속된 멤버들 간의 관계를 효율적으로 관리할 수 있게 해준다.</p>
</blockquote>
<h4 id="양방향">&lt;양방향&gt;</h4>
<p><img src="https://velog.velcdn.com/images/raymond_yoon/post/d2b8fe66-eb0a-470f-8e93-cc90884ace4d/image.png" alt=""></p>
<h4 id="객체-연관관계-2">객체 연관관계</h4>
<p>Member와 Team 객체가 서로 양방향으로 참조하고 있다. Member 객체는 Team team 필드를 통해 Team 객체를 참조하며, Team 객체는 List&lt; Member &gt; members 필드를 통해 여러 Member 객체들을 참조한다.
즉, 여러 Member가 하나의 Team에 속하고, Team은 자신에게 속한 모든 Member들을 참조할 수 있다.</p>
<h4 id="테이블-연관관계-2">테이블 연관관계</h4>
<p>데이터베이스에서는 MEMBER 테이블이 TEAM_ID 외래 키(FK)를 통해 TEAM 테이블과 연관되어 있다.
사진에서 표시된 것처럼, Team 객체에 추가된 members 필드는 테이블 구조에는 영향을 주지 않으며, 이는 객체 모델 상에서만 관리된다.</p>
<p>이 관계에서 Member 엔티티가 연관관계의 주인이다. 왜냐하면, 외래 키(TEAM_ID)가 MEMBER 테이블에 존재하기 때문이다. 연관관계 주인은 데이터베이스에서 실제로 외래 키를 관리하고, 해당 키를 업데이트하는 역할을 한다.</p>
<blockquote>
<p>이 구조는 Member와 Team 간의 관계를 양방향으로 설정하여, Member가 Team을 참조할 뿐만 아니라 Team도 자신에게 속한 Member들을 참조할 수 있도록 한다. 
이러한 양방향 관계는 데이터베이스 상에서 외래 키를 관리하는 주인 엔티티(Member)와 비주인 엔티티(Team)로 구분되어 구현된다.</p>
</blockquote>
<h3 id="일대다one-to-many-관계">일대다(One-to-Many) 관계</h3>
<h4 id="단방향-1">&lt;단방향&gt;</h4>
<p><img src="https://velog.velcdn.com/images/raymond_yoon/post/5390e563-49f1-4c84-92c4-dbe1510d902f/image.png" alt="">
<img src="https://velog.velcdn.com/images/raymond_yoon/post/0b191e95-6005-4952-ba50-71ff9fe1d594/image.png" alt=""></p>
<p>코드 구성을 보면 Team 클래스가 @OneToMany 어노테이션을 사용하여 Member들과 일대다 관계를 형성하고 있다.
@JoinColumn(name = &quot;TEAM_ID&quot;) 어노테이션은 members 리스트가 TEAM_ID 외래 키를 통해 Member들과 연관된다는 것을 나타낸다.
List&lt; Member &gt; members 필드를 통해 Team 객체는 여러 Member 객체를 관리할 수 있다.</p>
<h4 id="객체-연관관계-3">객체 연관관계</h4>
<p>Team 객체는 여러 Member 객체와 연결되며, 이 관계는 단방향이다. 즉, Team은 Member들을 참조하지만, Member는 Team을 참조하지 않는다.
관계가 단방향이기 때문에 Member 객체에서는 Team에 대한 정보가 없다.</p>
<h4 id="테이블-연관관계-3">테이블 연관관계</h4>
<p>데이터베이스에서 TEAM_ID는 MEMBER 테이블에 외래 키로 존재한다. 따라서 Member가 어떤 Team에 속해 있는지는 MEMBER 테이블의 TEAM_ID 컬럼을 통해 관리된다.</p>
<ul>
<li><p>주의사항 
&lt;외래 키 관리&gt;
@JoinColumn을 통해 지정된 외래 키는 반드시 MEMBER 테이블에 존재해야 하며, 이를 통해 Team과 Member 간의 연관관계가 설정된다.
team.getMembers().add(member) 메서드를 호출하지 않으면, Member의 TEAM_ID 필드가 null로 저장되어 관계가 설정되지 않는다.</p>
<p>&lt;Update 쿼리 발생&gt;
Team 객체에 Member 객체를 추가할 때마다 외래 키(TEAM_ID)를 업데이트하기 위한 쿼리가 발생합니다. 이로 인해 성능에 영향을 줄 수 있으므로, 이를 주의하여 관리해야 한다.</p>
<p>&lt;실수로 양방향 연관관계로 변환될 가능성&gt;
만약 Member 객체가 Team 객체를 참조해야 하는 상황이 발생한다면, 단방향에서 양방향 연관관계로 변경해야 할 수 있다. 하지만 이 경우 Member 클래스에 Team 필드를 추가하고 @ManyToOne 어노테이션을 붙여야 한다.</p>
<blockquote>
<p>이 구조는 Team 객체가 여러 Member 객체와 일대다 단방향 관계를 가지도록 설정한다. 데이터베이스 상에서 외래 키는 MEMBER 테이블에 존재하며, 이 관계는 객체 모델에서 단방향으로 관리된다. 이 방식은 단순하지만, 외래 키 관리 및 업데이트 쿼리에 주의해야 하며, 필요에 따라 양방향 관계로 확장할 수 있다.</p>
</blockquote>
</li>
</ul>
<p>&lt;양방향&gt;
<img src="https://velog.velcdn.com/images/raymond_yoon/post/5f4222df-695d-49ca-b61f-c1626f2eef63/image.png" alt="">
<img src="https://velog.velcdn.com/images/raymond_yoon/post/03f578ae-8e76-464e-ab55-29543cfd39a6/image.png" alt=""></p>
<p>코드구성을 보면 Member 클래스는 Team 클래스와 다대일(Many-to-One)로 연결되어 있으며, @ManyToOne 어노테이션을 통해 이를 설정한다.
@JoinColumn(name = &quot;TEAM_ID&quot;) 어노테이션은 Member 테이블에서 외래 키로 TEAM_ID를 사용함을 나타낸다.
insertable = false, updatable = false 설정은 이 필드를 읽기 전용으로 만들어, 데이터베이스에 값을 삽입하거나 수정할 수 없도록 제한한다. 이는 해당 관계가 양방향이지만, 데이터의 일관성을 유지하기 위해 한쪽에서만 외래 키를 관리하게 하려는 목적이다.</p>
<h4 id="객체-연관관계-4">객체 연관관계</h4>
<p>Team과 Member 간의 관계는 양방향이다. Team 클래스는 List&lt; Member &gt; 필드를 통해 여러 Member 객체들을 참조하고, Member 클래스는 Team 객체를 참조한다.
즉, Member가 어느 Team에 속하는지를 알 수 있으며, Team도 자신에게 속한 Member들을 알 수 있다.</p>
<h4 id="테이블-연관관계-4">테이블 연관관계</h4>
<p>데이터베이스에서는 MEMBER 테이블이 TEAM_ID 외래 키를 통해 TEAM 테이블과 연결되어 있다. 이 외래 키는 Member 엔티티에 의해 관리되지만, 해당 필드는 읽기 전용으로 설정되어있다.</p>
<blockquote>
<p>일대다 양방향 연관관계는 Team이 여러 Member를 참조하고, Member도 Team을 참조하는 구조를 가진다. 이 관계에서 Member 쪽에서 외래 키를 관리하지만, 읽기 전용으로 설정하여 데이터의 일관성을 유지하고, 특정 상황에서 관계를 안전하게 처리할 수 있도록 한다. 이를 통해 양방향 관계를 구현하되, 외래 키 관리를 한쪽에서만 책임지는 방식으로 설정할 수 있다.</p>
</blockquote>
<h3 id="다대다many-to-many-관계">다대다(Many-to-Many) 관계</h3>
<p><img src="https://velog.velcdn.com/images/raymond_yoon/post/3629dc9a-db01-4849-96bd-c391e714d95d/image.png" alt="">
<img src="https://velog.velcdn.com/images/raymond_yoon/post/dd834e9a-659f-4e65-ab21-6c8833006a05/image.png" alt=""></p>
<h4 id="객체-연관관계-5">객체 연관관계</h4>
<p>객체 모델에서는 Member와 Product 간의 다대다 관계를 List&lt; Product &gt;와 List&lt; Member &gt;를 사용하여 구현한다. 이를 통해 Member는 여러 Product를, Product는 여러 Member를 참조할 수 있다.
@ManyToMany 어노테이션을 사용하고, @JoinTable 어노테이션을 통해 중간 테이블(MEMBER_PRODUCT)을 설정하여 다대다 관계를 구현한다.</p>
<h4 id="테이블-연관관계-5">테이블 연관관계</h4>
<p>데이터베이스에서는 다대다 관계를 직접적으로 표현할 수 없기 때문에, 중간 테이블(연결 테이블)을 사용한다. 이 중간 테이블은 두 엔티티의 외래 키로 구성되며, 각각의 외래 키는 양쪽 엔티티와 일대다[1:N]관계를 맺는다.
예시에서는 Member_Product라는 중간 테이블을 통해 MEMBER와 PRODUCT 간의 다대다 관계를 구현하고 있다.</p>
<ul>
<li>주의사항 
&lt;실무에서의 사용&gt; 
실무에서는 다대다 관계를 직접 사용하는 것보다, 중간 테이블을 만들어 다대일 관계로 풀어내는 방식이 더 일반적이다. 이는 관리와 확장성을 높이고, 추가적인 컬럼을 필요로 할 때 유리하다.
&lt;성능 이슈&gt; 
다대다 관계를 직접 사용할 경우, 조인 쿼리가 복잡해지거나 성능 이슈가 발생할 수 있으므로, 필요에 따라 중간 테이블을 명시적으로 관리하는 것이 좋다.</li>
</ul>
<blockquote>
<p>다대다 관계는 두 엔티티 간의 다대다 연결을 설정하는 구조로, 이를 위해 중간 테이블을 사용하여 관계를 관리한다. 객체 모델에서는 @ManyToMany 어노테이션을 사용하며, 실무에서는 이를 다대일 관계로 풀어내어 관리하는 것이 일반적이다.</p>
</blockquote>
<hr>

<h2 id="요약">요약</h2>
<p>연관관계 매핑은 객체 지향 설계에서 엔티티 간의 관계를 명확하게 표현하고, 이를 데이터베이스의 관계형 모델과 일치시켜 데이터의 일관성과 무결성을 유지하는 데 필수적이다. 올바른 연관관계 매핑을 통해 코드의 가독성과 유지보수성을 높일 수 있으며, 복잡한 비즈니스 로직을 효율적으로 구현할 수 있다.</p>
<p>특히, 적절한 매핑 설계는 성능 최적화에 큰 영향을 미친다. 잘못된 매핑은 불필요한 쿼리 발생이나 N+1 문제를 초래할 수 있지만, 올바른 매핑은 쿼리의 효율성을 극대화하고 데이터베이스와의 상호작용을 최소화할 수 있다. 따라서 연관관계 매핑은 시스템의 성능과 유지보수성을 고려하여 신중하게 설계해야 한다. 이를 통해 안정적이고 확장 가능한 시스템을 구축할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[🦁멋쟁이사자처럼 백엔드 스터디 (1)]]></title>
            <link>https://velog.io/@raymond_yoon/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-%EB%B0%B1%EC%97%94%EB%93%9C-%EC%8A%A4%ED%84%B0%EB%94%94-1</link>
            <guid>https://velog.io/@raymond_yoon/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-%EB%B0%B1%EC%97%94%EB%93%9C-%EC%8A%A4%ED%84%B0%EB%94%94-1</guid>
            <pubDate>Sun, 02 Jun 2024 15:45:12 GMT</pubDate>
            <description><![CDATA[<p>이번년에 내 미래를 위해 뭐라도 해야한다는 생각으로 
작년에 기숙사 룸메이트로 만난 친구 덕분에 멋사라는 동아리를 알게되고, 
많이 부족한 실력이지만 들어가게 되었다. 
아무것도 모르고 방황하던 나에겐 그 친구가 시작점이 된 느낌이여서 고마웠다.</p>
<p>여튼 그래서 3월달부터 시작된 백엔드 스터디에 대해서 기록, 복습을 위해 velog에 기록하려고 한다.
내 미래, 꿈, 직업같은 것들은 velog에 기록하고, 취미인 클라이밍등등은 instagram, 일상은 blog에 저장하자는 목표가 있다. 추억하고 경험은 기록으로 남기는게 좋을 것 같다고 생각을 했다.</p>
<h1 id="백엔드-스터디-1">백엔드 스터디 (1)</h1>
<hr>

<h2 id="용어">용어</h2>
<h3 id="api">API</h3>
<p>API (Application Programing Interface)는 다른 소프트웨어 시스템과 통신하기 위해 따라야 하는 규칙을 정의하는 것을 말한다.</p>
<p>한마디로 어떠한 응용프로그램에서 데이터를 주고 받기 위한 방법을 의미하는 것 같다.</p>
<h3 id="인터페이스">인터페이스</h3>
<p>인터페이스는 어떤 기계간의 장치끼리 정보를 교환하기 위한 수단이나 방법을 의미한다.
일종의 매개체로 스마트폰과 사람간의 어떠한 신호를 주고 받는 매개체가 되는 소프트웨어인것같다.
UI(USER INTERFACE) 우리가 눈으로 보는 화면을 UI라 한다.</p>
<h3 id="jpa">JPA</h3>
<p>JPA (Java Persistence API)는 자바 애플리케이션에서 관계형 데이터베이스를 사용하는 표준방법을 제공하는 API이다.
JPA는 객체-관계 매핑(ORM) 기술을 사용해서 자바 객체를 데이터베이스 테이블에 매핑한다.</p>
<h3 id="jdbc">JDBC</h3>
<p>JDBC (Java Database Connectivity)는 자바 애플리케이션이 데이터베이스에 연결하고 SQL 쿼리를 실행하여 데이터를 검색하고 조작할 수 있게 하는 저수준 API 라고한다.
JDBC는 데이터베이스에 저장된 데이터를 자바 애플리케이션에서 사용할 수 있게 한다.</p>
<h3 id="orm">ORM</h3>
<p>ORM (Objcect-Relational Mapping)는 객체 지향 프로그래밍 언어의 객체를 관계형 데이터베이스의 테이블에 매핑하는 기술이다.
데이터베이스의 데이터를 자바 객체로 매핑하고, 자바 객체를 데이터베이스의 데이터로 매핑하여 프로그래밍 패러다임의 불일치를 해결한다.</p>
<hr>

<h4 id="jpa의-사용이유">JPA의 사용이유</h4>
<ol>
<li><p>패러다임의 불일치 해결</p>
<ul>
<li>객체 지향 프로그래밍 언어와 관계형 데이터베이스의 데이터 모델 간의 불일치를 해소한다.</li>
</ul>
</li>
<li><p>객체 중심적 개발</p>
<ul>
<li>SQL 중심의 개발 방식에서 벗어나 객체 중심으로 개발할 수 있게 한다.</li>
</ul>
</li>
<li><p>생산성 향상</p>
<ul>
<li>반복적인 SQL 코드 작성에서 해방되어 비즈니스 로직에 집중할 수 있게 한다.</li>
</ul>
</li>
<li><p>성능 개선</p>
<ul>
<li>1차 캐시, 지연로딩, 쓰기 지연등의 최적화 기능을 통해 성능을 개선한다.</li>
</ul>
</li>
<li><p>유지보수 용이</p>
<ul>
<li>객체 지향 모델을 사용하여 코드의 가독성과 유지보수성을 높인다.</li>
</ul>
</li>
</ol>
<h2 id="구동방식-gpt예시">구동방식 (GPT예시)</h2>
<h4 id="1-프로젝트-설정">1. 프로젝트 설정</h4>
<p>build.gradle:</p>
<pre><code>plugins {
    id &#39;org.springframework.boot&#39; version &#39;2.6.6&#39;
    id &#39;io.spring.dependency-management&#39; version &#39;1.0.11.RELEASE&#39;
    id &#39;java&#39;
}

group = &#39;com.example&#39;
version = &#39;0.0.1-SNAPSHOT&#39;
sourceCompatibility = &#39;11&#39;

repositories {
    mavenCentral()
}

dependencies {
    implementation &#39;org.springframework.boot:spring-boot-starter-data-jpa&#39;
    implementation &#39;org.springframework.boot:spring-boot-starter-web&#39;
    runtimeOnly &#39;org.mariadb.jdbc:mariadb-java-client&#39;
    testImplementation &#39;org.springframework.boot:spring-boot-starter-test&#39;
}

test {
    useJUnitPlatform()
}
</code></pre><h4 id="2-applicationproperties-설정">2. application.properties 설정</h4>
<p>src/main/resources/application.properties 파일을 생성하고, MariaDB 데이터베이스 연결 정보를 설정한다.</p>
<p>application.properties:</p>
<pre><code>spring.datasource.url=jdbc:mariadb://localhost:3306/your_database
spring.datasource.username=your_username
spring.datasource.password=your_password
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver

spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MariaDBDialect
</code></pre><h4 id="3-엔티티-클래스-정의">3. 엔티티 클래스 정의</h4>
<p>데이터베이스 테이블에 매핑될 자바 클래스를 정의한다.</p>
<p>User.java:</p>
<pre><code>package com.example.demo;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class User {

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

    // Getters and setters
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}
</code></pre><h4 id="4-리포지토리-인터페이스-생성">4. 리포지토리 인터페이스 생성</h4>
<p>JPA를 사용하여 CRUD 작업을 수행할 리포지토리를 정의한다.</p>
<p>UserRepository.java:</p>
<pre><code>package com.example.demo;

import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository&lt;User, Long&gt; {
}
</code></pre><h4 id="5-서비스-클래스-구현">5. 서비스 클래스 구현</h4>
<p>비즈니스 로직을 처리하는 서비스 클래스를 구현한다.</p>
<p>UserService.java:</p>
<pre><code>package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    public User saveUser(User user) {
        return userRepository.save(user);
    }

    public List&lt;User&gt; getAllUsers() {
        return userRepository.findAll();
    }

    public User getUserById(Long id) {
        return userRepository.findById(id).orElse(null);
    }

    public void deleteUser(Long id) {
        userRepository.deleteById(id);
    }
}
</code></pre><h4 id="6-컨트롤러-클래스-구현">6. 컨트롤러 클래스 구현</h4>
<p>HTTP 요청을 처리하는 컨트롤러 클래스를 구현한다.</p>
<p>UserController.java:</p>
<pre><code>package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping(&quot;/users&quot;)
public class UserController {

    @Autowired
    private UserService userService;

    @PostMapping
    public ResponseEntity&lt;User&gt; createUser(@RequestBody User user) {
        User createdUser = userService.saveUser(user);
        return new ResponseEntity&lt;&gt;(createdUser, HttpStatus.CREATED);
    }

    @GetMapping
    public ResponseEntity&lt;List&lt;User&gt;&gt; getAllUsers() {
        List&lt;User&gt; users = userService.getAllUsers();
        return new ResponseEntity&lt;&gt;(users, HttpStatus.OK);
    }

    @GetMapping(&quot;/{id}&quot;)
    public ResponseEntity&lt;User&gt; getUserById(@PathVariable Long id) {
        User user = userService.getUserById(id);
        return user != null ? new ResponseEntity&lt;&gt;(user, HttpStatus.OK) : new ResponseEntity&lt;&gt;(HttpStatus.NOT_FOUND);
    }

    @DeleteMapping(&quot;/{id}&quot;)
    public ResponseEntity&lt;Void&gt; deleteUser(@PathVariable Long id) {
        userService.deleteUser(id);
        return new ResponseEntity&lt;&gt;(HttpStatus.NO_CONTENT);
    }
}
</code></pre><h4 id="7-애플리케이션-실행">7. 애플리케이션 실행</h4>
<p>DemoApplication.java:</p>
<pre><code>package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}
</code></pre><p>이제 애플리케이션을 실행하면, Spring Boot 애플리케이션이 시작되고 MariaDB 데이터베이스에 연결됩니다. Postman 또는 다른 HTTP 클라이언트를 사용하여 CRUD 작업을 수행할 수 있다.</p>
<h4 id="postman을-사용한-예제">Postman을 사용한 예제</h4>
<ol>
<li><p>Create User (POST 요청)</p>
<p>URL: <a href="http://localhost:8080/users">http://localhost:8080/users</a>
Method: POST
Body (raw, JSON)</p>
<pre><code>{
  &quot;name&quot;: &quot;John Doe&quot;,
  &quot;email&quot;: &quot;john.doe@example.com&quot;
}
</code></pre></li>
<li><p>Get All Users (GET 요청)</p>
<p>URL: <a href="http://localhost:8080/users">http://localhost:8080/users</a>
Method: GET
Get User by ID (GET 요청)</p>
<p>URL: <a href="http://localhost:8080/users/1">http://localhost:8080/users/1</a>
Method: GET
Delete User (DELETE 요청)</p>
<p>URL: <a href="http://localhost:8080/users/1">http://localhost:8080/users/1</a>
Method: DELETE</p>
</li>
</ol>
<p>이와 같은 방법으로 Gradle과 MariaDB를 사용하여 Spring Boot 애플리케이션을 설정하고, JPA를 활용하여 간단한 CRUD 애플리케이션을 구현할 수 있다.</p>
<p>나중에는 스터디에서 한 프로젝트로 활용해보겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[물이 흐르기 시작했다.]]></title>
            <link>https://velog.io/@raymond_yoon/%EB%AC%BC%EC%9D%B4-%ED%9D%90%EB%A5%B4%EA%B8%B0-%EC%8B%9C%EC%9E%91%ED%96%88%EB%8B%A4</link>
            <guid>https://velog.io/@raymond_yoon/%EB%AC%BC%EC%9D%B4-%ED%9D%90%EB%A5%B4%EA%B8%B0-%EC%8B%9C%EC%9E%91%ED%96%88%EB%8B%A4</guid>
            <pubDate>Tue, 16 Jan 2024 16:08:33 GMT</pubDate>
            <description><![CDATA[<p>이제 4학년이 되어버린 나</p>
<p>준비된 포폴도 없고 실력도 없는 만큼 더 열심히 해야한다.
시작하기를 두려워 했지만 시간은 흐르고 이제 물도 엎질러졌다.
오늘부로 물은 흐르기 시작한다.</p>
<p>첫 게시글은 미래의 내가 볼 수 있고, 동기부여가 될 수 있게 자유 형식으로 쓰고싶다.
나의 최종목표는 사람좋은 기업들어가기... 꿈은 크게 가질수록 좋다고 했다.</p>
<p>목표를 크게 잡아 실패하는 것은 문제가 되지 않는다고 생각한다.
오히려 목표를 낮게 잡아 실패하는것이 문제라고 생각한다.</p>
<p>이번년 나의 목표</p>
<ol>
<li>마지막 나의 학창시절을 잘 즐기는것</li>
<li>사회로 나가기전에 준비 단단히 하기</li>
<li>포트폴리오 쌓고, 실력 쌓고, 추억 쌓기</li>
<li>인간관계 힘들어하지 않기</li>
<li>부모님께 잘하기</li>
</ol>
<p>나중에 학교를 졸업하고 다시보면 새로운 느낌일거라 생각한다.</p>
<p>다들 엄청난 속도로 나의 앞에서 가고있지만, 
조금은 의식을 하되, 
나의 속도에 맞춰서 날 믿고 가자.</p>
<p>이 글을 기점으로 다시 일어나자
파이팅..!!</p>
<p>사진은 내가 좋아하는 망그러진 곰 행운부적</p>
]]></description>
        </item>
    </channel>
</rss>