<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>so_oyv.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Fri, 28 Jun 2024 06:20:42 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>so_oyv.log</title>
            <url>https://velog.velcdn.com/images/so_oyv/profile/a3e38353-47b8-402a-8625-fbfa8bcca349/image.PNG</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. so_oyv.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/so_oyv" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Apache, Tomcat에 대해서 알아보기]]></title>
            <link>https://velog.io/@so_oyv/Apache-Apache-Tomcat%EC%9D%98-%EC%B0%A8%EC%9D%B4</link>
            <guid>https://velog.io/@so_oyv/Apache-Apache-Tomcat%EC%9D%98-%EC%B0%A8%EC%9D%B4</guid>
            <pubDate>Fri, 28 Jun 2024 06:20:42 GMT</pubDate>
            <description><![CDATA[<p>Apache와 Tomcat은 웹을 공부해본 사람이라면 알고있는, 적어도 한번쯤은 들어본 이름일 것이다.
오늘은 이 개념에 대해서 정리해보고자 한다.</p>
<h2 id="web-server와-was">Web Server와 WAS</h2>
<p>아파치를 알아보기 전에 Web Server와 Web Application Server에 대한 이해가 필요하다.</p>
<h3 id="1-web-server">1. Web Server</h3>
<p>Web Server는 보통 <strong>HTTP 서버</strong>를 의미한다. HTTP 서버란 <strong>URL(웹 주소)</strong> 및 <strong>HTTP(프로토콜 주소)</strong>를 이해하는 소프트웨어이다. 
HTTP 서버는 저장하는 웹 사이트의 <strong>도메인 이름</strong>을 통해 액세스 할 수 있으며 이러한 호스팅 된 웹 사이트의 콘텐츠를 최종 사용자의 장치로 전달한다.</p>
<h3 id="1-1-web-server의-동작">1-1. Web Server의 동작</h3>
<blockquote>
<ol>
<li>클라이언트에서 HTTP를 통해 파일을 요청한다. </li>
<li>요청이 올바른 Web 서버에 도달하면 HTTP 서버가 요청을 수락하고 요청된 문서를 찾는다.</li>
<li>찾는 문서를 HTTP를 통해 브라우저로 다시 반환한다. 
(이때, 요청된 문서를 찾지 못한다면, ‘404 Not Found’ 를 반환)</li>
</ol>
</blockquote>
<p>여기서 Web Server의 역할은 <strong>정적인 자료</strong>를 처리하는 서버이다.</p>
<p><strong>HTML, CSS, IMAGE 등 정적 파일들을 서버에 저장하고, 요청시마다 서버에 저장된 파일을 사용자에게 HTML로 반환</strong>해준다.
그렇기 때문에 Web Server는 <strong>서버 자원의 한계</strong>가 생기고, <strong>리소스를 많이 차지</strong>한다는 단점이 발생한다.</p>
<h3 id="2-web-application-server-was">2. Web Application Server (WAS)</h3>
<p>위의 웹 서버의 단점을 해결하고자 나온 것이 WAS이다. 
<img src="https://velog.velcdn.com/images/so_oyv/post/67ec244d-470a-4178-9f98-8d178af41563/image.png" alt=""></p>
<p>WAS는 <strong>웹서버 + 웹 컨테이너</strong>로 구성되어 있다. 여기서 웹 컨테이너는 Servlet과 JSP를 실행할 수 있는 서블릿 컨테이너(Servlet Container)이며, <strong>동적 컨텐츠(Servlet, JSP)를 처리</strong>한다.</p>
<p>WAS는 웹서버와 웹 컨테이너의 결합으로 다양한 기능을 컨테이너에 구현하여 다양한 역할을 수행할 수 있는 서버를 말한다.</p>
<h3 id="2-1-was의-동작">2-1. WAS의 동작</h3>
<blockquote>
<ol>
<li>클라이언트에서 HTTP 요청을 한다.</li>
<li>해당 요청은 우선 <strong>Web Server</strong>에서 정적데이터만 필요한 요청인지 확인한다. </li>
<li>확인 결과 정적 데이터만이 필요하다면, 그대로 요청에 대한 응답을 클라이언트에게 보내준다.</li>
<li>만약 동적 데이터가 필요한 요청이라면, 웹 컨테이너(서블릿 컨테이너)에 넘겨준다.</li>
<li><strong>웹 컨테이너</strong>는 요청정보를 파악하여 실시간으로 페이지에 필요한 파일을 생성하여 클라이언트에 반환한다.</li>
</ol>
</blockquote>
<p>이처럼 <strong>웹 컨테이너</strong>는 클라이언트 요청에서 실시간으로 결과를 만들어내고 이것을 다시 클라이언트에 전달해주는 역할을 한다.</p>
<p>Web Server와 WAS의 가장 큰 차이점은 이 <strong>웹 컨테이너가 있느냐 없느냐</strong>의 차이이다.</p>
<h2 id="apache와-tomcat">Apache와 Tomcat</h2>
<p>Web Server와 WAS에 대해 알아보았으니, Apache와 Tomcat은 무엇인지 알아보자.</p>
<h3 id="apache">Apache</h3>
<p><img src="https://velog.velcdn.com/images/so_oyv/post/b81b093b-b32c-49c1-9e56-1df91485227e/image.png" alt=""></p>
<p><strong>Apache</strong>는 아파치 소프트웨어 재단의 오픈소스 프로젝트로, HTTP Request를 처리하는 <strong>웹 서버</strong>이다. 클라이언트 요청이 왔을때만 응답하는 <strong>정적 웹페이지</strong>에 사용된다. 즉, <strong>정적 데이터(HTML, CSS, IMAGE)</strong>만을 처리한다.</p>
<h3 id="tomcat">Tomcat</h3>
<p><img src="https://velog.velcdn.com/images/so_oyv/post/d2ce8a7f-d2ec-45d2-a415-d0ca4ab134ad/image.png" alt=""></p>
<p><strong>Tomcat</strong>은 흔히 <strong>WAS(Web Application Server)</strong>라고 한다.</p>
<p><strong>동적인 웹(Dynamic Web)</strong>을 만들기 위한 웹 컨테이너, JSP와 Servlet을 구동하기 위한 서블릿 컨테이너 역할을 수행한다. 
웹서버에서 정적으로 처리해야할 데이터를 제외한 JSP, ASP, PHP 등은 웹 컨테이너(톰캣)에게 전달한다.
<strong>동적인 데이터 처리</strong>가 가능하고, DB 연결, 데이터 조작, 다른 응용 프로그램과 상호 작용이 가능하다.</p>
<h2 id="apache-tomcat이란">Apache-Tomcat이란?</h2>
<p>많은 사람들이 <strong>Apache-Tomcat은 사실 Tomcat</strong>을 말하는 것이다. 위에서 알아봤듯이, 아파치와 톰캣의 기능은 나뉘어져 있지만 Tomcat안의 <strong>웹 컨테이너</strong>를 통해 일부 Apache의 기능을 하기 때문에, <strong>Apache-Tomcat</strong>이라는 이름으로 불리게 되었다.</p>
<h4 id="tomcat에서-apache의-기능을-한다면-tomcat만을-사용하면-안되는-것일까">Tomcat에서 Apache의 기능을 한다면, Tomcat만을 사용하면 안되는 것일까?</h4>
<p>웹페이지는 정적 데이터뿐만 아니라 동적 데이터도 함께 존재한다.
만약 Tomcat에서 정적 데이터와 동적 데이터 모두를 처리하게 한다면, 그만큼 <strong>부하</strong>도 커질 것이고, <strong>응답 또한 느려질 가능성</strong>이 크다. (WAS는 정적 데이터를 바로 반환하는 Web Server보다 데이터 처리 속도가 느리다.) 
그래서 정적 데이터는 아파치가 동적 데이터는 톰캣이 분산 처리하는 것이다.</p>
<h2 id="정리">정리</h2>
<hr>
<blockquote>
<p><strong>Apache</strong> - 정적인 파일을 처리해주는 Web Server (80 포트)
<strong>Tomcat</strong> - DB처리와 같은 동적인 기능들을 가공하여 HTML 파일로 만들어 클라이언트에게 제공하는 WAS (8080 포트)</p>
</blockquote>
<blockquote>
<p><strong>Web Server</strong> - HTTP, HTTPS를 통해 웹 브라우저에서 요청하는 HTML 문서나 오브젝트(이미지 파일 등의 정적 콘텐츠)를 전송해주는 서비스 프로그램
<strong>Web Application Server</strong> - 서버 계층에서 애플리케이션이 동작할 수 있는 환경을 제공하고, 안정적인 트랜잭션 처리와 관리, 다른 시스템과의 애플리케이션 연동을 지원하는 서버로 동적 콘텐츠(Sevlet, JSP)를 처리하기 위해 사용</p>
</blockquote>
<h3 id="추가">추가</h3>
<h4 id="웹-서버의-종류">웹 서버의 종류</h4>
<ul>
<li>Apache</li>
<li>Google Web Server</li>
<li>Nginx</li>
<li>ISS</li>
</ul>
<h4 id="웹-어플리케이션-서버의-종류">웹 어플리케이션 서버의 종류</h4>
<ul>
<li>Tomcat</li>
<li>WebLogic</li>
<li>Jeus</li>
<li>Resin</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[테이블 설계와 연관관계 매핑(단방향, 양방향)]]></title>
            <link>https://velog.io/@so_oyv/%ED%85%8C%EC%9D%B4%EB%B8%94-%EC%84%A4%EA%B3%84%EC%99%80-%EC%97%B0%EA%B4%80%EA%B4%80%EA%B3%84-%EB%A7%A4%ED%95%91</link>
            <guid>https://velog.io/@so_oyv/%ED%85%8C%EC%9D%B4%EB%B8%94-%EC%84%A4%EA%B3%84%EC%99%80-%EC%97%B0%EA%B4%80%EA%B4%80%EA%B3%84-%EB%A7%A4%ED%95%91</guid>
            <pubDate>Thu, 20 Jun 2024 07:40:16 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>본 내용은 인프런의 <a href="https://www.inflearn.com/course/ORM-JPA-Basic/dashboard">자바 ORM 표준 JPA 프로그래밍</a> - 기본편 강의를 수강하고 정리한 글입니다.</p>
</blockquote>
<p>JPA에서 가장 중요한 것 중 하나는 <strong>연관관계 매핑</strong>이라고 할 수 있습니다. 이는 &quot;<strong>객체와 테이블을 매핑하여 패러다임 불일치 문제를 해결</strong>&quot;하고자 만들어진 JPA의 사용 목적과도 일치합니다.</p>
<hr>
<h2 id="객체와-테이블의-연관관계-차이">객체와 테이블의 연관관계 차이</h2>
<blockquote>
<p>테이블 - 외래키를 통한 JOIN으로 관계 설정
객체 - 참조를 통한 관계 설정</p>
</blockquote>
<p>우선 객체와 데이터베이스 테이블간의 차이를 이해하고 이 둘 사이의 매핑이 어떻게 이루어지는지 이해하는 것이 중요합니다.
테이블의 경우, 외래키를 통해서 테이블 간 연관관계를 설정합니다. 그에 반해 객체는 참조를 통해서 관계를 맺습니다.</p>
<p>아래의 예제를 통해 객체의 참조와 테이블의 외래키를 어떻게 매핑하는 것이 올바른지 알아보겠습니다.</p>
<h2 id="객체를-테이블에-맞추어-모델링">객체를 테이블에 맞추어 모델링</h2>
<p><img src="https://velog.velcdn.com/images/so_oyv/post/ee9664df-77e3-4773-9980-4932f11cd45f/image.jpeg" alt="">
객체를 테이블에 맞추어 모델링 할 경우엔 MEMBER 테이블에 <code>TEAM_ID</code>의 외래키를 갖게 되고, 참조가 아닌 외래키를 그대로 사용하게 됩니다.</p>
<p><strong>MEMBER</strong></p>
<pre><code class="language-java">@Entity
public class Member {
    @Id @GenerateValue
    private Long id;

    private int age;

    @Column(name = &quot;USERNAME&quot;)
    private String name;

    @Column(name = &quot;TEAM_ID&quot;)
    private Long teamId;

    ...
}</code></pre>
<p><strong>TEAM</strong></p>
<pre><code class="language-java">@Entity
public class Team {
    @Id @GenerateValue
    private Long id;

    private String name;
    ...
}</code></pre>
<p>만약 찾은 멤버가 어느 팀 소속인지 알고 싶다면 어떻게 코드를 작성해야 할까요?</p>
<pre><code class="language-java">Member findMember = em.find(Member.class, member.getId());

Long findTeamId = findMember.getTeamId();

Team findTeam = em.find(Team.class, findTeamId);</code></pre>
<ol>
<li>멤버를 찾고</li>
<li>멤버에서 팀 아이디를 찾아오고</li>
<li>찾아온 팀 아이디로 팀을 찾아야 합니다.</li>
</ol>
<p>이처럼 객체를 테이블에 맞추어 데이터 중심으로 모델링하면, 협력관계를 만들 수 없습니다.
테이블은 단지 외래키 <code>team_id</code>를 가지고 있을뿐 객체끼리 어떠한 관계설정이 되어있지 않기 때문에, 바로 <code>TEAM</code> 객체를 찾는 것이 불가능합니다. 이러한 설계는 어떤 것을 조회할때 항상 데이터베이스에 접근해야하기때문에 비효율적이며, <strong>객체지향적이지 않다</strong>고 할 수 있습니다.</p>
<p>그렇다면 어떻게 작성하는 것이 옳은 방법일까요?</p>
<h2 id="용어-이해">용어 이해</h2>
<p>이를 위해서 이해해야하는 기본 용어는 크게 3가지가 있습니다.
그 중 오늘은 단방향 연관관계와 양방향 연관관계에 대해서 알아보도록 하겠습니다.</p>
<blockquote>
<ul>
<li><strong>방향</strong>(Direction) : 단방향, 양방향</li>
</ul>
</blockquote>
<ul>
<li><strong>다중성</strong> (Muliplicity) : 다대일(N:1), 일대다(1:N), 일대일(1:1), 다대다(N:M)</li>
<li><strong>연관관계의 주인</strong> (Owner) : 객체지향 양방향 연관관계는 관리 주인이 필요</li>
</ul>
<h3 id="1-단방향-연관관계">1. 단방향 연관관계</h3>
<p> 데이터베이스 테이블의 경우 외래키 하나로 양쪽 테이블의 조인이 가능합니다. 그렇기 때문에 <strong>데이터베이스 테이블에는 방향이라는 개념이 존재하지 않</strong>습니다.
그러나 <strong>객체는 참조하는 객체를 가진 쪽에서만 다른 객체를 참조하는 것이 가능</strong>합니다. 이처럼 한 방향으로 객체를 참조하는 것을 <strong>단방향</strong> 관계라고 합니다.
<img src="https://velog.velcdn.com/images/so_oyv/post/76ea0635-e5ee-40f8-a69b-3fdce4ec9d03/image.jpeg" alt="">
표에서 보듯이, <code>MEMBER</code>객체는 <strong>외래키가 아닌 <code>TEAM</code> 참조값을 그대로 가지고</strong> 있는 것을 볼 수 있습니다.
여기서 <code>MEMBER</code>는 <code>TEAM</code>을 참조하고 있기 때문에 접근이 가능하지만, <code>TEAM</code>에서 <code>MEMBER</code>는 확인이 불가능한 <strong>단방향 연관관계</strong>입니다. 즉, <code>member.getTeam()</code>은 가능하지만, <code>team.getMember()</code>는 불가능합니다.
이것을 코드로 표현하면 어떻게 하면 될까요?</p>
<pre><code class="language-java">// Member
@Entity
public class Member {

    @Id @GenerateValue
    private Long id;

    @Column(name = &quot;USERNAME&quot;)
    private String name;

    private int age;

    // @Column(name = &quot;TEAM_ID&quot;)
    // private Long teamId;

    @ManyToOne
    @JoinColumn(name = &quot;TEAM_ID&quot;)
    private Team team;
    ...
}


// TEAM
@Entity
public class Team {

    @Id @GenerateValue
    private Long id;

    private String name;
    ...
}</code></pre>
<p>하나의 팀에 여러명의 member가 소속되니까, MEMBER 쪽에 <code>@ManyToOne</code>으로 다대일 연관관계를 설정해줍니다. 해당 어노테이션을 통해서 어느쪽이 N인지 JPA에게 관리하도록 해줍니다.
<code>@JoinColumn(name = &quot;TEAM_ID&quot;)</code>으로 외래키 team_id와 매핑해줍니다. 외래키 <code>TEMA_ID</code>를 직접 사용했던 테이블 중심 설계와 다르게 <code>@JoinColumn</code> 어노테이션을 사용해 매핑해 줍니다.</p>
<h4 id="연관관계-저장">연관관계 저장</h4>
<pre><code class="language-java">// 팀 저장
Team team = new Team();
team.setName(&quot;teamA&quot;);
em.persist(team);

// 회원 저장
Member member = new Member();
member.setName(&quot;member1&quot;);
member.setTeam(team);    // 단방향 연관관계 설정, 참조 저장
em.persist(member);
</code></pre>
<p>이처럼 member에 team_id가 아닌 team 객체 자체를 넣어줍니다.</p>
<h4 id="연관관계-조회">연관관계 조회</h4>
<pre><code class="language-java">// 조회
Member findMember = em.find(Member.class, member.getId());

// 참조를 통해서 연관관계 조회
Team findTeam = findMember.getTeam();</code></pre>
<p>데이터 중심 설계의 조회 방식과 다르게, <code>findMember.getTeam()</code>을 통해 객체 그래프 탐색이 가능하게 됩니다.</p>
<h4 id="수정">수정</h4>
<pre><code class="language-java">// 새로운 팀B
Team teamB = new Team();
teamB.setName(&quot;TeamB&quot;);
em.persist(teamB);

// 회원 1에 새로운 팀B 설정
member.setTeam(teamB);</code></pre>
<h3 id="2-양방향-연관관계">2. 양방향 연관관계</h3>
<p> 양방향 관계란 <strong>양쪽에서 참조하는 객체를 가져, 서로 참조가 가능</strong>한 것을 의미합니다.
엄밀히 말하면 양방향 관계라는 것은, 각각 객체를 참조하고 있는 <strong>두 개의 단방향 관계</strong>를 의미합니다.</p>
<p><img src="https://velog.velcdn.com/images/so_oyv/post/727eca57-3065-4471-a91e-844ebb557ade/image.jpeg" alt=""></p>
<p>Member 객체에는 Team 참조값이 있고, Team 객체에는 member를 List로 양방향에서 참조값을 갖습니다. </p>
<pre><code class="language-java">// Member
@Entity
public class Member {

    @Id @GenerateValue
    private Long id;

    @Column(name = &quot;USERNAME&quot;)
    private String name;

    private int age;

    // @Column(name = &quot;TEAM_ID&quot;)
    // private Long teamId;

    @ManyToOne
    @JoinColumn(name = &quot;TEAM_ID&quot;)
    private Team team;
    ...
}

// Team
@Entity
public class Team {

    @Id @GenerateValue
    private Long id;

    private String name;

    @OneToMany(mappedBy = &quot;team&quot;)
    private List&lt;Member&gt; members = new ArrayList&lt;&gt;();
    ...
}</code></pre>
<p>MEMBER와 TEAM은 (N:1)의 연관관계를 갖기 때문에, Team 쪽에는 <code>@OneToMany</code> 어노테이션을 통해 매핑해주면 됩니다. 
<code>mappedBy</code>는 현재 1 : N 매핑에서 무엇과 연결되어 있는지 나타내주는 것으로 반대쪽 매핑의 필드명을 적어주면 됩니다. Member의 team과 매핑되어있음을 나타내줍니다.</p>
<pre><code class="language-java">// 조회
Member findMember = em.find(Member.class, member.getId);
List&lt;Member&gt; members = findMember.getTeam().getMembers();

for (Member member : members) {
System.out.println(&quot;m = &quot; + member.getUsername());
}

// 결과
// m = member1
// m = member2</code></pre>
<p>이렇게 양방향 모두로 객체 그래프 탐색이 가능하게 되었습니다.</p>
<h3 id="연관관계의-주인-mappedby-설정">연관관계의 주인, mappedBy 설정</h3>
<p>사실, 두 객체가 서로 양방향 연관관계를 맺는다는 것은 <strong>두 개의 단방향 연관관계가 있다는 것</strong>을 의미합니다.
위의 코드에서도 보았다시피, 회원에서 팀으로 참조값 하나, 팀에서 회원으로 참조값이 존재합니다. 서로 다른 단방향 연관관계 2개를 양방향인 것처럼 보이게 할 뿐입니다. 반면에 <strong>데이터베이스 테이블은 외래 키 하나로 양쪽에서 조인이 가능</strong>합니다.</p>
<p>이렇게 데이터베이스 테이블과 객체가 관계를 맺는 차이를 해결하기 위해서 JPA에서는 <strong>연관관계의 주인을 지정</strong>해주어야합니다.</p>
<p><code>mappedBy</code>는 양방향 연관관계의 주인을 지정합니다. 객체의 두 관계중 하나를 연관관계의 주인으로 지정합니다. <strong>연관관계의 주인만이 외래 키를 관리(등록, 수정) 할 수 있</strong>으며, <strong>주인이 아닌 쪽은 읽기만 가능</strong>합니다.</p>
<blockquote>
<p><code>mappedBy</code> 속성은 연관관계의 주인이 아닌 객체에 사용합니다. mappedBy라는 이름 그대로, 어느쪽에 매핑되었는지를 표현해줍니다.</p>
</blockquote>
<h4 id="그렇다면-연관관계의-주인을-어떻게-지정해야-할까요">그렇다면 연관관계의 주인을 어떻게 지정해야 할까요?</h4>
<p>연관관계의 주인은 <strong>외래 키를 가지고 있는 쪽</strong>으로 하면 됩니다.</p>
<p><strong>Fk를 가지고 있는 쪽이 연관관계의 주인</strong>이며, &#39;진짜 매핑&#39;이라고 합니다. 주인의 반대편은 &#39;가짜매핑&#39;이 되는 것이죠.
데이터베이스 테이블의 관계로 생각했을때는 외래키가 있는쪽이 N, 없는 쪽은 1이기 때문에, N쪽을 연관관계의 주인으로 생각하면 됩니다.</p>
<h3 id="양방향-연관관계의-주의점">양방향 연관관계의 주의점</h3>
<p>만약 연관관계의 주인이 아닌쪽에 값을 넣으면 어떤 문제가 발생할까요? </p>
<pre><code class="language-java">Team team = new Team();
team.setName(&quot;TeamA&quot;);
em.persist(team);

Member member = new Member();
member.setName(&quot;member1&quot;);

// 역방향(주인이 아닌 방향)에만 연관관계 설정
team.getMambers().add(member);

em.persist(member);</code></pre>
<p>member1을 저장하고 TEAM의 컬렉션에 담은 후 저장했습니다. 여기에서 member1은 제대로 저장이 될까요?
위 코드를 실행하고 데이터베이스에서 회원 테이블을 조회하면 다음과 같은 결과가 나옵니다.</p>
<table>
<thead>
<tr>
<th align="center">MEMBER_ID</th>
<th align="center">USERNAME</th>
<th align="center">TEAM_ID</th>
</tr>
</thead>
<tbody><tr>
<td align="center">member1</td>
<td align="center">회원1</td>
<td align="center">null</td>
</tr>
</tbody></table>
<p>외래 키 TEAM_ID에 null 값이 들어가있는 것을 볼 수 있습니다.
양방향 연관관계를 설정할때, <strong>연관관계의 주인만이 외래 키를 관리(등록, 수정) 할 수 있으며, 주인이 아닌 쪽은 읽기만 가능</strong>하다고 했습니다.
해당 코드에서는 연관관계의 주인이 아닌 역방향에만 값을 넣었기 때문에 위와 같은 문제가 발생한 것입니다. 가짜매핑에 값을 넣으면 JPA는 읽기만 가능합니다.</p>
<p>반대로 연관관계의 주인에 값을 넣어보면 아래와 같이 MEMBER의 TEAM_ID가 올바르게 들어간 것을 볼 수 있습니다.</p>
<pre><code class="language-java">Team team = new Team();
team.setName(&quot;TeamA&quot;);
em.persist(team);

Member member = new Member();
member.setName(&quot;member1&quot;);
member.setTeam(team);

em.persist(member);</code></pre>
<table>
<thead>
<tr>
<th align="center">MEMBER_ID</th>
<th align="center">USERNAME</th>
<th align="center">TEAM_ID</th>
</tr>
</thead>
<tbody><tr>
<td align="center">member1</td>
<td align="center">회원1</td>
<td align="center">1</td>
</tr>
</tbody></table>
<h3 id="순수한-객체-관계를-고려한-연관관계-매핑">순수한 객체 관계를 고려한 연관관계 매핑</h3>
<p>그렇다면 정말 연관관계의 주인에만 값을 넣어주면 되는 걸까요?</p>
<p>가장 권장되는 방법은 <strong>양쪽 모두에 값을 입력</strong>하는 것입니다. </p>
<pre><code class="language-java">Team team = new Team();
team.setName(&quot;TeamA&quot;);
em.persist(team);

Member member = new Member();
member.setName(&quot;member1&quot;);
member.setTeam(team);        // 양쪽에 값 세팅
em.persist(member);

team.getMembers().add(member);    // 양쪽에 값 세팅
</code></pre>
<p>JPA에서 양방향 매핑 시 양쪽 모두에 연관관계를 설정하는 이유는 JPA가 JAVA 객체 관점과 데이터베이스 테이블 관점에서 관계를 관리하기 때문입니다.</p>
<p>연관관계의 주인은 외래키를 관리하며, 연관관계의 주인이 아닌쪽은 읽기만 가능합니다. 이로 인해 연관관계의 주인쪽에서만 <code>setTeam()</code>과 같은 메서드를 사용하여 연관관계를 설정하면 JPA가 이를 테이블에 반영할 수 있으나, JAVA 객체 상에서는 양쪽 모두에 반영할 필요가 있습니다.
그렇기 때문에 <strong>테스트 케이스 작성 시와 같은 JPA를 사용하지 않을때</strong> 문제가 발생합니다. 이때는 JPA 없이 순수 자바 코드를 통해 테스트를 하기 때문에 양쪽 모두에 값을 세팅해주지 않으면 양방향 연관관계를 올바르게 사용할 수 없습니다.</p>
<p>따라서, JAVA <strong>객체 상의 일관성을 유지하고 순수한 객체 관계를 고려</strong>하여 양쪽 모두에 설정하는 것이 좋습니다.</p>
<h3 id="연관관계-편의-메서드">연관관계 편의 메서드</h3>
<p>연관관계 편의 메서드는 양쪽 모두에 연관관계를 세팅해야하는 불편함을 해소하고자 사용하는 방법입니다.</p>
<p>위의 코드와 같이 <code>member.setTeam(team);</code>, <code>team.getMembers().add(member);</code> 처럼 각각 코드를 작성하다보면, 한쪽에만 작성한다던지하는 실수가 발생할 수 있습니다. </p>
<pre><code class="language-java">public class Member {

  private Team team;

  public void setTeam(Team team) {
    this.team = team;
    team.getMembers().add(this);    // 연관관계 편의
  }

  ...
}</code></pre>
<p>이렇게 <code>setTeam()</code> 메서드를 연관관계 편의 메서드로 변경해 양방향 관계를 설정할 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JPA의 영속성 컨텍스트(Persistence context)]]></title>
            <link>https://velog.io/@so_oyv/JPA%EC%9D%98-%EC%98%81%EC%86%8D%EC%84%B1-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8Persistence-context</link>
            <guid>https://velog.io/@so_oyv/JPA%EC%9D%98-%EC%98%81%EC%86%8D%EC%84%B1-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8Persistence-context</guid>
            <pubDate>Tue, 04 Jun 2024 15:48:02 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>본 내용은 인프런의 <a href="https://www.inflearn.com/course/ORM-JPA-Basic/dashboard">자바 ORM 표준 JPA 프로그래밍</a> - 기본편 강의를 수강하고 정리한 글입니다.</p>
</blockquote>
<h2 id="영속성-컨텍스트란">영속성 컨텍스트란?</h2>
<p>영속성 컨텍스트란 <strong>&quot;엔티티를 영구 저장하는 환경&quot;</strong> 이라는 뜻이다. 쉽게 생각하자면, 영속성 컨텍스트는 애플리케이션과 데이터베이스 사이에서 객체를 보관하는 가상의 DB라고 생각하면 된다.</p>
<pre><code class="language-java">EntityManager.persist(object)</code></pre>
<ul>
<li><code>persist()</code>를 통해서 영속성 컨텍스트에 객체를 저장한다.</li>
<li>엔티티 매니저를 통해서 영속성 컨텍스트에 접근하고 관리할 수 있다.</li>
<li>엔티티 매니저를 생성할때 하나 만들어진다.</li>
</ul>
<h2 id="엔티티의-생명주기">엔티티의 생명주기</h2>
<p>엔티티는 EntityManager를 통해 영속성 컨텍스트에 저장이 된다. 하지만, 이는 실제 DB가 아니기때문에 생명주기를 갖는다.</p>
<h3 id="1-비영속newtransient">1. 비영속(new/transient)</h3>
<p>영속성 컨텍스트와 전혀 관계가 없는 새로운 상태</p>
<pre><code class="language-java">Member member = new Member()
member.setId(1L);
member.setName(&quot;홍길동&quot;);</code></pre>
<p>엔티티 객체를 생성했지만,영속성 컨텍스트에 저장하지 않은 상태이다.</p>
<h3 id="2-영속managed">2. 영속(managed)</h3>
<p>영속성 컨텍스트에 관리되는 상태</p>
<pre><code class="language-java">// 객체를 생성한 상태
Member member = new Member()
member.setId(1L);
member.setName(&quot;홍길동&quot;);

EntityManager em = emf.createEntityManager();
em.getTransaction().begin();

// 객체를 영속화
em.persist(member);</code></pre>
<p><code>em.persist(member)</code>를 통해서 Member 객체를 <strong>영속화</strong> 하였다. 이를 통해 Member 객체는 영속성 컨텍스트에 의해서 관리된다.</p>
<h3 id="3-준영속detached">3. 준영속(detached)</h3>
<p>영속성 컨텍스트에 저장되었다가 분리된 상태</p>
<pre><code class="language-java">em.detach(member);</code></pre>
<p>회원 엔티티를 영속성 컨텍스트에서 분리하여 더 이상 영속성 컨텍스트에 의해서 관리되지 않도록 한다.</p>
<h3 id="4-삭제removed">4. 삭제(removed)</h3>
<p>삭제된 상태</p>
<pre><code class="language-java">em.remove(member);</code></pre>
<p>엔티티를 영속성 컨텍스트와 데이터베이스에서 삭제한다.</p>
<h2 id="영속성-컨텍스트의-장점">영속성 컨텍스트의 장점</h2>
<blockquote>
<ol>
<li>1차 캐시</li>
<li>동일성(identity) 보장</li>
<li>트랜잭션을 지원하는 쓰기 지연 (Transactional write-behind)</li>
<li>변경 감지 (Dirty checking)</li>
<li>지연 로딩 (Lazy Loading)</li>
</ol>
</blockquote>
<h3 id="1-1차-캐시">1. 1차 캐시</h3>
<p>영속성 컨텍스트의 내부에는 <strong>1차 캐시</strong> 라는 것이 있다. 영속성 컨텍스트에 의해 관리되는 영속 상태의 엔티티를 여기에 저장한다. 
이 1차 캐시에는 키와 값이 존재하는데, 여기서 키는 데이터베이스의 PK(식별자 값)이며, 값은 Entity 객체 자체이다.</p>
<h4 id="1-1-1차-캐시에서-조회-방법">1-1. 1차 캐시에서 조회 방법</h4>
<pre><code class="language-java">Member member = new Member()
member.setId(1L);
member.setName(&quot;홍길동&quot;);

// 1차 캐시에 저장
em.persist(member);

// 1차 캐시에서 조회
Member findMember = em.find(Member.class, 1L);</code></pre>
<p>위의 코드를 살펴보면 조회의 흐름을 다음과 같다.</p>
<ol>
<li>데이터베이스가 아닌 1차 캐시에서 조회한다.</li>
<li>조회하는 값이 있으면, 엔티티를 반환한다.</li>
<li>조회하는 값이 없으면, 데이터베이스에서 조회한다. (이때는 select 쿼리 전송)</li>
<li>데이터베이스에서 찾은 엔티티를 1차 캐시에 저장한다.</li>
<li>엔티티를 반환한다.</li>
</ol>
<p>위의 흐름을 통해 알 수 있듯이, 만약 1차 캐시에 1L번의 member 객체가 존재하지 않아 데이터베이스에서 엔티티를 찾아왔다면, <strong>이후 1번 member 객체를 다시 찾을때는 DB가 아닌 1차 캐시에서 찾아</strong>오는 것을 알 수가 있다.
물론 데이터베이스에 SELECT 쿼리도 전송되지 않는다.</p>
<h3 id="동일성identity-보장">동일성(identity) 보장</h3>
<pre><code class="language-java">Member a = em.find(Member.class, &quot;101L&quot;);
Member b = em.find(Member.class, &quot;101L&quot;);

System.out.print(a == b)     // true</code></pre>
<p>먼저 위와 같이 조회를 했을 때, 아래와 같이 한번의 쿼리가 전송된다. 
<img src="https://velog.velcdn.com/images/so_oyv/post/7cfac2cc-28ba-473d-a484-f06879d334e3/image.png" alt=""></p>
<p>위에서 알아봤듯이, 1차캐시에서 조회했지만 존재하지 않은 101번 객체는, 데이터베이스에서 조회하게 되며 1차 캐시에 저장되었다. 이로 인해서 a객체를 조회할 때 1번의 조회 쿼리만 동작한 것이다.</p>
<p>이와 같이 1차 캐시가 있기 때문에 마치 자바 컬렉션에서 꺼낸 것과 같이 동일성 보장이 되며, a와 b의 비교 결과가 true로 나온 것이다.</p>
<h3 id="3-트랜잭션을-지원하는-쓰기-지연-transactional-write-behind">3. 트랜잭션을 지원하는 쓰기 지연 (Transactional write-behind)</h3>
<p>쓰기 지연이란 생성한 쿼리를 SQL 저장소에 모아두었다가 트랜잭션 commit() 시점에 플러시가 되면서 데이터베이스에 sql을 전송하는 것을 말한다.
<img src="https://velog.velcdn.com/images/so_oyv/post/5f6629dd-44f8-48c5-8d26-4632003a322c/image.png" alt=""></p>
<h4 id="쓰기-지연-흐름">쓰기 지연 흐름</h4>
<pre><code class="language-java">EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();

transaction.begin(); // [트랜잭션] 시작

em.persist(memberA);
em.persist(memberB);

transaction.commit();</code></pre>
<p>영속성 컨텍스트 안에는 <strong>1차 캐시</strong>와 <strong>쓰기 지연 SQL 저장소</strong>가 존재한다.
객체를 persist( )하면, 1차 캐시에 들어가는 동시에, JPA는 이 엔티티를 분석하면서 쿼리를 생성한다.
이렇게 생성한 쿼리는 쓰기 지연 SQL 저장소에 쌓아둔다. </p>
<p>코드를 보자면, <code>em.persist(memberA);</code>로 memberA 객체가 1차 캐시에 들어갔다. 이와 동시에 JPA는 memberA 객체를 분석해 INSERT 쿼리를 작성한다. 작성한 쿼리는 쓰기 지연 SQL 저장소에 둔다.
<code>em.persist(memberB);</code> 다음 memberB 객체가 persist( )되면서 1차 캐시와 쓰기 지연 SQL 저장소에 쿼리가 쌓이게 된다.</p>
<p>이후 <code>transaction.commit();</code>이 되면서 <strong>쓰기 지연 SQL 저장소에 있던 쿼리가 모두 데이터베이스에 반영</strong>된다.</p>
<p><img src="https://velog.velcdn.com/images/so_oyv/post/4316f6c3-ca2f-4b75-bbcd-5b803f5017ec/image.png" alt=""><em>추가적으로 hibernate.batch_size라는 것을 이용하면 사이즈만큼 모아서 한번에 네트워크로 전송할 수도 있다.</em></p>
<h3 id="4-변경-감지-dirty-checking">4. 변경 감지 (Dirty checking)</h3>
<p>변경 감지라는 것은 JPA에서 엔티티를 수정할 때의 변경 방식을 말한다.</p>
<pre><code class="language-java">Member member = em.find(Member.class, 150L);
member.setName(&quot;zzz&quot;);

System.out.println(&quot;==================&quot;);

// 트랜잭션 commit
tx.commit();</code></pre>
<p>150번 Member 객체를 찾아서 zzz로 name을 수정해주었다. 단순히 <code>set()</code>으로 값을 수정하고 <code>persist()</code>나 다른 무언가를 해주지 않아도 데이터베이스에 update 쿼리가 전송되는 것을 볼 수 있다.
<img src="https://velog.velcdn.com/images/so_oyv/post/8b7f15e1-1eb6-4255-a5e0-9e614f118832/image.png" alt=""></p>
<p>1차 캐시에는 이전에 설명한 pk, 엔티티와 <strong>스냅샷</strong>이 있다.</p>
<blockquote>
<p>스냅샷 - 최초 영속성 컨텍스트에 들어온 1차 캐시 상태</p>
</blockquote>
<p>이 스냅샷은 값을 읽어온 최초시점의 상태를 떠두는 것이다. 
JPA는 데이터베이스 트랜잭션을 커밋하는 시점에 내부적으로 플러시가 호출 되는데, 이 과정에서 엔티티와 스냅샷을 비교한다.
변경된 값이 존재하면 업데이트 쿼리를 쓰기 지연 SQL 저장소에 만들어 두고 플러시 한 뒤, 데이터베이스 트랜잭션을 커밋한다.</p>
<p>이러한 과정을 통해서 persist( ) 할 필요 없이 커밋 시점에 엔티티가 관리되는 것이다.</p>
<h2 id="플러시-flush">플러시 (Flush)</h2>
<p>플러시는 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영하는 것을 말한다. 보통 데이터베이스 트랜잭션이 커밋될 때 이 플러시라는 것이 일어난다.
앞에서 보았듯이 쌓아둔 SQL 쿼리들을 플러시를 통해서 영속성 컨텍스트의 현재 변경사항과 데이터베이스를 맞춰주는 것이다. </p>
<h4 id="플러시-흐름">플러시 흐름</h4>
<ol>
<li>데이터베이스 트랜잭션 커밋이 발생하면 플러시는 자동으로 일어난다.</li>
<li>변경 감지 (Dirty checking)</li>
<li>수정된 엔티티를 쓰기지연 SQL 저장소에 등록</li>
<li>쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송(등록, 수정, 삭제 쿼리)</li>
</ol>
<p>데이터베이스에 쿼리 전송 -&gt; 데이터베이스 트랜잭션 커밋 순서로 동작한다. </p>
<h4 id="영속성-컨텍스트를-플러시하는-방법">영속성 컨텍스트를 플러시하는 방법</h4>
<ol>
<li>em.flush - 직접 호출</li>
<li>트랜잭션 커밋 - 플러시 자동 호출</li>
<li>JPQL 쿼리 실행 - 츨러시 자동 호출</li>
</ol>
<h4 id="코드로-알아보기">코드로 알아보기</h4>
<pre><code class="language-java">Member member = new Member(200L, &quot;member200&quot;);
em.persist(member);

em.flush();

System.out.println(&quot;==================&quot;);

tx.commit();</code></pre>
<p>위의 코드의 결과로는 아래와 같은 결과가 나온다.
<img src="https://velog.velcdn.com/images/so_oyv/post/7d30199d-a162-4e11-a97f-bb4ab2a39437/image.png" alt="">
원래대로라면 commit하는 시점에 쿼리가 전송되기 때문에 &quot;=========&quot; 선 아래에 INSERT 쿼리가 작성되어야 하지만, 그 전에 flush를 강제 호출 했기때문에 INSERT 쿼리가 즉시 전송된 것이다.</p>
<h4 id="플러시-정리">플러시 정리</h4>
<ol>
<li>플러시를 한다고 해서 1차 캐시가 지워지지는 않는다. 즉, 영속성 컨텍스트를 비우는 것이 아니다.</li>
<li>영속성 컨텍스트의 변경 내용을 데이터베이스에 동기화시키는 것이다.</li>
<li>트랜잭션이라는 작업 단위가 중요 -&gt; 커밋 직전에 동기화</li>
</ol>
<hr>
<p>이렇게 JPA의 영속성 컨텍스트에 대해서 알아보았다. 영속성 컨텍스트는 JPA를 이해하는데 가장 중요한 용어이다. 눈에 보이지 않고 논리적인 개념으로 어렵게 느껴졌던 영속성 컨텍스트를 이렇게 정리함으로 잘 이해할 수 있었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JPA의 구동방식]]></title>
            <link>https://velog.io/@so_oyv/JPA%EC%9D%98-%EA%B5%AC%EB%8F%99%EB%B0%A9%EC%8B%9D</link>
            <guid>https://velog.io/@so_oyv/JPA%EC%9D%98-%EA%B5%AC%EB%8F%99%EB%B0%A9%EC%8B%9D</guid>
            <pubDate>Fri, 31 May 2024 06:06:40 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>본 내용은 인프런의 <a href="https://www.inflearn.com/course/ORM-JPA-Basic/dashboard">자바 ORM 표준 JPA 프로그래밍</a> - 기본편 강의를 수강하고 정리한 글입니다.</p>
</blockquote>
<h1 id="jpa의-동작-방식을-알아보자">JPA의 동작 방식을 알아보자!</h1>
<p>위의 그림을 보고 설명하자면, 
먼저 JPA는 Persistence 클래스가 존재한다. 이 클래스는 개발자가 작성한 persistence.xml에서 설정 정보를 읽어와 EntityManagerFactiory라는 클래스를 만든다. 
이렇게 만든 EntityManagerFactory에서는 뭔가 필요할 때마다 EntityManager를 찍어내 만든다.</p>
<h2 id="🔗-코드로-확인하기">🔗 코드로 확인하기</h2>
<pre><code class="language-java">public class JpaMain {

    public static void main(String[] args) {

        EntityManagerFactory emf = Persistence.createEntityManagerFactory(&quot;hello&quot;);
        EntityManager em = emf.createEntityManager();

        em.close();
        emf.close();
    }
}</code></pre>
<p>해당 코드를 처음부터 자세히 확인해보자.</p>
<pre><code class="language-java">public class JpaMain {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory(&quot;&quot;)
}</code></pre>
<p>Persistence라는 클래스의 <code>createEntityManagerFactory()</code>를 통해서 EntityManagerFactory를 반환해 준다.
여기에서 createEntityManagerFactory()안에는 PersistenceUnitName을 넘기라고 한다.
<img src="https://velog.velcdn.com/images/so_oyv/post/158231d6-dede-4099-818f-9d85b14de292/image.png" alt=""></p>
<p>이것은 persistence.xml 파일의 persistence-unit에 설정한 name을 넘겨주면 된다.</p>
<pre><code class="language-java">public class JpaMain {

    public static void main(String[] args) {

        EntityManagerFactory emf = Persistence.createEntityManagerFactory(&quot;hello&quot;);
        EntityManager em = emf.createEntityManager();

        em.close();
        emf.close();
    }
}</code></pre>
<p>다음은 EntityManagerFactory에서 createEntityManager()를 꺼낸다. 
실제 애플리케이션이 끝나면 EntityManagerFactory를 닫아주기 위해서 <code>close();</code> 해준다.</p>
<h2 id="🔍-동작-확인하기">🔍 동작 확인하기</h2>
<p>위의 내용을 코드를 통해 확인해보자!</p>
<h3 id="저장하기">저장하기</h3>
<p>데이터베이스에 Member 테이블을 생성해주고, JPA에 엔티티를 추가해준다.
그리고 아래와 같이 member를 생성해준다. </p>
<pre><code class="language-java">    public static void main(String[] args) {

        EntityManagerFactory emf = Persistence.createEntityManagerFactory(&quot;hello&quot;);
        EntityManager em = emf.createEntityManager();

        Member member = new Member();
        member.setId(1L);
        member.setName(&quot;HelloA&quot;);

        em.persist(member);

        em.close();
        emf.close();
    }</code></pre>
<p>하지만 이렇게 실행할 경우 에러가 발생하는데, JPA는 데이터를 변경하는 작업은 반드시 <strong>Transaction 안에서 작업</strong>해야 하기 때문이다.</p>
<p>여기서 중요한 것은 <strong>엔티티 매니저 팩토리는 애플리케이션 로딩 시점에 하나만 생성</strong>하여야 하고, 트랜잭션 단위는 DB 커넥션을 얻어 쿼리를 날리고 종료되는 단위마다 엔티티 매니저를 통해 실행된다. 간단하게 생각하자면 엔티티 매니저는 DB 커넥션이라고 할 수 있다.</p>
<pre><code class="language-java">public static void main(String[] args) {

        EntityManagerFactory emf = Persistence.createEntityManagerFactory(&quot;hello&quot;);
        EntityManager em = emf.createEntityManager();

        EntityTransaction tx = em.getTransaction();
        tx.begin();

        Member member = new Member();
        member.setId(1L);
        member.setName(&quot;HelloA&quot;);

        em.persist(member);
        tx.commit();

        em.close();
        emf.close();
    }</code></pre>
<ol>
<li>엔티티 매니저에서 <code>getTransaction()</code>으로 트랜잭션을 얻는다.</li>
<li><code>begin()</code> - 트랜잭션 시작</li>
<li>member를 만들고</li>
<li><code>em.persist(member)</code>로 member를 저장</li>
<li><code>tx.commit()</code> - 커밋</li>
</ol>
<p><img src="https://velog.velcdn.com/images/so_oyv/post/916edaf4-0dbf-4d6a-9c1f-89d9c45f9380/image.png" alt="">
<img src="https://velog.velcdn.com/images/so_oyv/post/2c3d81c8-2f91-4839-9228-821352be38df/image.png" alt="">
쿼리와 데이터베이스에 helloA 멤버가 저장된 것을 확인할 수 있다.</p>
<p>또한 이것으로 확인 할 수 있는 것은, 내가 직접 쿼리를 작성할 필요 없이, <strong>JPA가 매핑정보를 통해 모든 것을 해준다</strong>는 것이다. </p>
<hr>
<p>위와 같이 코드를 작성할 경우 문제 발생 가능성이 존재한다. 
만약 <code>em.persist(member);</code> 나  <code>tx.commit();</code> 시점에 문제가 생기게 되면, EntityManager <code>close()</code>와 EntityManagerFactory <code>close()</code>가 모두 호출되지 않기 때문이다.</p>
<p>따라서 try-catch 문을 통해 문제가 일어났을 경우에도 코드가 올바르게 동작하도록 수정해주어야 한다.</p>
<pre><code class="language-java">    public static void main(String[] args) {

        EntityManagerFactory emf = Persistence.createEntityManagerFactory(&quot;hello&quot;);
        EntityManager em = emf.createEntityManager();

        EntityTransaction tx = em.getTransaction();
        tx.begin();

        try {
            Member member = new Member();
            member.setId(2L);
            member.setName(&quot;HelloB&quot;);

            em.persist(member);
            tx.commit();

        } catch (Exception e) {
            tx.rollback();

        } finally {
            em.close();
        }
        emf.close();
    }</code></pre>
<ol>
<li>try문 안에 시도할 코드를 넣어준다. Member를 생성하고, <code>commit()</code>한다. </li>
<li>만약 이 과정까지에서 문제가 발생한다면 catch문에서 Exception이 발생하고 <code>rollback()</code> 한다.</li>
<li>작업이 끝나면 finally문에서 EntityManager를 닫아준다. 
EntityManager가 결국 내부적으로 데이터베이스 커넥션을 얻어 동작하는 것이기 때문에 반드시 닫아주는 것을 잊지 말자.</li>
<li>전체 애플리케이션이 끝나면 EntityManagerFactory까지 <code>close()</code>해준다.</li>
</ol>
<p>이렇게 JPA의 동작에 대해서 알아보았다. 하지만 실제로는 이렇게 EntityManager를 생성할 필요 없이 spring이 이러한 것을 해주기 때문에 호출해서 더욱 편하게 사용할 수 있다. </p>
<h3 id="조회하기">조회하기</h3>
<pre><code class="language-java"> public static void main(String[] args) {

        EntityManagerFactory emf = Persistence.createEntityManagerFactory(&quot;hello&quot;);
        EntityManager em = emf.createEntityManager();

        EntityTransaction tx = em.getTransaction();
        tx.begin();

        try {
            Member findMember = em.find(Member.class, 1L);
            System.out.println(&quot;찾은 회원 이름 : &quot;+ findMember.getName());

            tx.commit();
        } catch (Exception e) {
            tx.rollback();

        } finally {
            em.close();
        }
        emf.close();
    }</code></pre>
<ol>
<li>EntityManager에서 find()해준다. 이 엔티티 매니저는 자바 컬렉션과 같이 이해하면 된다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/so_oyv/post/882b263f-0326-4473-afc9-f48ff04de348/image.png" alt="">
select 쿼리로 회원을 찾은 것을 볼 수 있다.</p>
<h3 id="삭제하기">삭제하기</h3>
<pre><code class="language-java"> public static void main(String[] args) {

        EntityManagerFactory emf = Persistence.createEntityManagerFactory(&quot;hello&quot;);
        EntityManager em = emf.createEntityManager();

        EntityTransaction tx = em.getTransaction();
        tx.begin();

        try {
            Member findMember = em.find(Member.class, 1L);

            em.remove(findMember);

            tx.commit();
        } catch (Exception e) {
            tx.rollback();

        } finally {
            em.close();
        }
        emf.close();
    }</code></pre>
<p>remove(); 메소드 안에 삭제할 멤버를 넣어주면 Delete 쿼리가 나가면서 삭제된다. </p>
<h3 id="수정하기">수정하기</h3>
<pre><code class="language-java">  public static void main(String[] args) {

        EntityManagerFactory emf = Persistence.createEntityManagerFactory(&quot;hello&quot;);
        EntityManager em = emf.createEntityManager();

        EntityTransaction tx = em.getTransaction();
        tx.begin();

        try {
            Member findMember = em.find(Member.class, 1L);
            findMember.setName(&quot;HelloJPA&quot;);

            tx.commit();
        } catch (Exception e) {
            tx.rollback();

        } finally {
            em.close();
        }
        emf.close();
    }</code></pre>
<p>set() 메서드로 변경할 이름으로 바꾸어준다. 
여기서 em.persist로 변경한 이름을 저장해주어야 하는게 아닌가? 라는 생각을 할 수도 있지만, 저장하지 않아도 된다.</p>
<p><img src="https://velog.velcdn.com/images/so_oyv/post/3cec1dbd-5861-4529-8a75-9bf891336720/image.png" alt="">쿼리를 확인해보면 UPDATE 쿼리가 나간 것을 볼 수 있다. </p>
<p><img src="https://velog.velcdn.com/images/so_oyv/post/77ff8ee4-6c73-4628-ada1-53f39800dced/image.png" alt="">데이터베이스에 변경한 이름인 helloJPA로 이름이 변경된 것을 확인했다.</p>
<p>그렇다면 저장하지 않았음에도 어떻게 해서 이름을 변경할 수 있는 것일까?
그 이유는 JPA를 통해서 엔티티를 가져오게 되면, 가져온 엔티티는 JPA가 관리를 하게 된다. 이후 트랜잭션 커밋 시점에 관리되는 엔티티는 변경 사항을 체크한다. 트랜잭션이 커밋하기 직전에 UPDATE 쿼리를 실행해 변경사항을 수정하고 트랜잭션이 커밋된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[javascript - replaceAll()]]></title>
            <link>https://velog.io/@so_oyv/javascript-replaceAll</link>
            <guid>https://velog.io/@so_oyv/javascript-replaceAll</guid>
            <pubDate>Mon, 26 Jun 2023 08:33:51 GMT</pubDate>
            <description><![CDATA[<p><strong>String.prototype.replaceAll()</strong></p>
<p>프로그래머스 문제를 풀다가 replaceAll()이라는 메소드를 발견하여 알아보았다. 또한 replace()와의 차이가 궁금해 정리해 보았다.</p>
<hr>
<h3 id="문제명-rny_string">문제명: rny_string</h3>
<p>먼저 해당 코딩테스트 문제는 다음과 같다.</p>
<h3 id="문제-설명"><strong>문제 설명</strong></h3>
<p>&#39;m&#39;과 &quot;rn&quot;이 모양이 비슷하게 생긴 점을 활용해 문자열에 장난을 하려고 합니다. 문자열 <code>rny_string</code>이 주어질 때, <code>rny_string</code>의 모든 &#39;m&#39;을 &quot;rn&quot;으로 바꾼 문자열을 return 하는 solution 함수를 작성해 주세요.</p>
<h3 id="제한사항">제한사항</h3>
<ul>
<li>1 ≤ <code>rny_string</code>의 길이 ≤ 100</li>
<li><code>rny_string</code>은 영소문자로만 이루어져 있습니다.</li>
</ul>
<h3 id="입출력-예">입출력 예</h3>
<table>
<thead>
<tr>
<th>rny_string</th>
<th>result</th>
</tr>
</thead>
<tbody><tr>
<td>&quot;masterpiece&quot;</td>
<td>&quot;rnasterpiece&quot;</td>
</tr>
<tr>
<td>&quot;programmers&quot;</td>
<td>&quot;prograrnrners&quot;</td>
</tr>
<tr>
<td>&quot;jerry&quot;</td>
<td>&quot;jerry&quot;</td>
</tr>
<tr>
<td>&quot;burn&quot;</td>
<td>&quot;burn&quot;</td>
</tr>
</tbody></table>
<p>즉, 문자열에 m이 있으면 그것을 rn으로 바꾸어 return 하는 것이다.</p>
<h3 id="👉-해당-문제-풀이-보러가기">👉 해당 문제 풀이 보러가기</h3>
<p><a href="https://github.com/sooyv/Programmers-Algorithm-JS/commit/455734a9270b72a5371d2a824c8761461d6285c3">https://github.com/sooyv/Programmers-Algorithm-JS/commit/455734a9270b72a5371d2a824c8761461d6285c3</a></p>
<hr>
<p>먼저 replaceAll()에 대한 MDN의 설명을 찾아보았다. MDN에 따르면 설명은 다음과 같다.</p>
<aside>

<h3 id="구문">구문</h3>
<p><code>replaceAll(pattern, replacement)</code> </p>
<h3 id="매개변수">매개변수</h3>
<p><strong><code>pattern</code></strong></p>
<blockquote>
<p>문자열이거나 <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/replace"><code>Symbol.replace</code>(en-US)</a> 메서드가 있는 객체일 수 있습니다. 일반적인 예로 <a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/RegExp">정규식</a>이 있습니다. <code>Symbol.replace</code> 메서드가 없는 모든 값은 문자열로 강제 변환됩니다.</p>
</blockquote>
<blockquote>
<p><code>pattern</code>이 정규식이면 전역(<code>g</code>) 플래그가 설정되어 있어야 합니다. 그렇지 않으면 <a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/TypeError"><code>TypeError</code></a>가 발생합니다.</p>
</blockquote>
<p><strong><code>replacement</code></strong></p>
<blockquote>
<p>문자열이거나 함수일 수 있습니다. 교체는 <a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/String/replace"><code>String.prototype.replace()</code></a>와 동일한 의미 체계를 갖습니다.</p>
</blockquote>
<h3 id="반환값">반환값</h3>
<p>패턴의 모든 일치 항목이 교체자로 대체된 새 문자열입니다.</p>
</aside>


<hr>
<p><code>replaceAll()</code>은 <code>String객체</code>에 추가된 새로운 메소드이다. 역할은 문자열에서 특정 문자를 지정한 문자열로 변경하는 역할을 한다.</p>
<p>이것과 비슷한 역할을 하는 메소드로 replace()메소드가 있는데, <code>replace()</code>는 <strong>정규식을 통해 찾은 문자열을 지정한 문자열로 바꾸는</strong> 역할을 한다. </p>
<p>하지만 <code>replaceAll()</code>을 이용하면, 문자열을 직접 지정해서 바꿔버릴수 있기 때문에 더욱 편리하게 사용이 가능하다.</p>
<h2 id="replace">replace()</h2>
<p>또한, 공부를 하던 중 replace()에 대해 더욱 자세히 알 수 있었다. replace()의 예시를 보자.</p>
<p><img src="https://velog.velcdn.com/images/so_oyv/post/b325594d-6de7-4917-97b4-b0acd0c908fc/image.png" alt=""></p>
<p>위와 같이, “m”을 “rn”으로 바꾸었을 때, “mom”의 “rnom”은 바뀌지 않았다. 즉, replace()의 경우 첫번째 문자만 변경이 되는 것을 확인하였다.</p>
<h2 id="replaceall">replaceAll()</h2>
<p>하지만 replaceAll()의 경우 해당 문자열 전체를 바꾼다.</p>
<p><img src="https://velog.velcdn.com/images/so_oyv/post/491dd444-9071-4d8b-920b-b6f8916b0a18/image.png" alt=""></p>
<p>이렇게 편리하게 사용할 수 있는 replaceAll()이지만, ES2021이 도입되지 않은 곳에서는 메서드를 사용 할 수 없다는 문제점이 있었다. 따라서 또 다른 방법으로 replaceAll()을 구현해 보았다.</p>
<h2 id="replaceall-구현하기">replaceAll() 구현하기</h2>
<p>replaceAll()을 사용하기 위한 방법은 split()과 join()을 사용하는 것이다.</p>
<ul>
<li>split을 통해 전체 문자열을 작성한 문자열을 기준으로 나눠 배열로 저장한다.</li>
</ul>
<pre><code class="language-js">let str = &quot;hello mom&quot;;
str = str.split(&quot;m&quot;);
console.log(str);
// [&#39;hello &#39;, &#39;o&#39;, &#39;&#39;]</code></pre>
<ul>
<li>join()을 사용하여 만든 배열을 하나의 문자열로 합쳐준다.</li>
</ul>
<p>‘m’을 “rn”으로 바꿔줘야하는 것이기 때문에, 배열 사이에 “rn”을 추가한다. </p>
<pre><code class="language-js">let str = &quot;hello mom&quot;;
str = str.split(&quot;m&quot;);
str = str.join(&quot;rn&quot;);
console.log(str);
// hello rnorn</code></pre>
<hr>
<aside>

<h2 id="정리">정리</h2>
<pre><code> 문자열.split(”찾을 문자열”).join(”바꿀 문자열”);</code></pre><p>과 같이 replaceAll를 대체하여 사용할 수 있다.</p>
<pre><code class="language-jsx">let str = &quot;hello mom&quot;;
str = str.split(&quot;m&quot;).join(&quot;rn&quot;);
console.log(str);
// hello rnorn</code></pre>
</aside>


<hr>
<h1 id="👉-해당-문제-풀이-보러가기-1">👉 해당 문제 풀이 보러가기</h1>
<p><a href="https://github.com/sooyv/Programmers-Algorithm-JS/commit/455734a9270b72a5371d2a824c8761461d6285c3">https://github.com/sooyv/Programmers-Algorithm-JS/commit/455734a9270b72a5371d2a824c8761461d6285c3</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[javascript - reduce()]]></title>
            <link>https://velog.io/@so_oyv/javascript-reduce</link>
            <guid>https://velog.io/@so_oyv/javascript-reduce</guid>
            <pubDate>Mon, 19 Jun 2023 09:22:11 GMT</pubDate>
            <description><![CDATA[<p><strong>Array.prototype.reduce()</strong></p>
<p>오늘은 프로그래머스 문제 풀이 중, 꼬리 문자열 문제에서 reduce()를 사용한 다른 사람의 풀이를 보았다. </p>
<p>평소 자주 접하는 함수이지만, 헷갈리던 부분에 대해서 명확하게 알고 사용하고자 reduce()함수에 대해 정리해보았다.🧐</p>
<hr>
<p>reduce 함수는 “줄이다”라는 의미로 누산기라고 생각하면 된다. </p>
<p><strong>리듀서</strong> 함수는 네 개의 인자를 가진다.</p>
<ol>
<li>누산기 (acc)</li>
<li>현재 값 (cur)</li>
<li>현재 인덱스 (idx)</li>
<li>원본 배열 (src)</li>
</ol>
<h2 id="구문">구문</h2>
<blockquote>
<p><code>arr.reduce(callback[, initialValue])</code></p>
</blockquote>
<h2 id="매개변수">매개변수</h2>
<blockquote>
<p><code>callback</code></p>
<p>배열의 각 요소에 대해 실행할 함수. 다음 네 가지 인수를 받는다.</p>
<p><code>accumulator</code> </p>
<p>누산기는 콜백의 반환값을 누적한다. 콜백의 이전 반환값 또는, 콜백의 첫 번째 호출이면서 <code>initialValue</code>를 제공한 경우에는 <code>initialValue</code> 값</p>
<p><code>currentValue</code></p>
<p>처리할 현재 요소. </p>
<p><code>currentIndex</code></p>
<p>Optional처리할 현재 요소의 인덱스. <code>initialValue</code>를 제공한 경우 0, 아니면 1부터 시작</p>
<p><code>array</code> </p>
<p><code>reduce()</code>를 호출한 배열.</p>
<p><code>initialValue</code> </p>
<p><code>callback</code>의 최초 호출에서 첫 번째 인수에 제공하는 값. 초기값을 제공하지 않으면 배열의 첫 번째 요소를 사용하고 빈 배열에서 초기값 없이 <code>reduce()</code>를 호출하면 오류가 발생</p>
</blockquote>
<h2 id="사용방법">사용방법</h2>
<h3 id="1-가장-기본적인-형태">1. 가장 기본적인 형태</h3>
<p>두 개의 인자
    - <code>acc</code> accumulator : 누산기, 누적되는 값, 최종적으로 출력되는 값
    - <code>cur</code> current : 현재 돌고 있는 요소</p>
<pre><code class="language-js">const numbers = [1, 2, 3, 4];
let sum = numbers.reduce((acc, cur) =&gt; acc + cur);</code></pre>
<p>이것을 풀어서 보면 다음과 같다.</p>
<pre><code class="language-js">const numbers = [1, 2, 3, 4];
let sum = numbers.reduce((acc, cur) =&gt; {
    return acc + cur;
});</code></pre>
<p>동작 흐름을 자세히 보자면 이렇다.</p>
<p><code>acc</code> 에는 가장 첫 번째 원소인 &#39;1&#39;를 할당하고 (index 0) <code>cur</code> 에는 나머지 원소인 &#39;2, 3, 4&#39;가 순차적으로 들어간다. </p>
<pre><code class="language-js">acc += cur; // 1 += 2;
acc += cur; // 3 += 3;
acc += cur; // 6 += 4;

console.log(acc); // 10</code></pre>
<h3 id="2-초기값이-추가된-형태">2. 초기값이 추가된 형태</h3>
<p>2개의 인자 + 1개의 초기값
    - <code>acc</code> accumulator : 누산기, 누적되는 값, 최종적으로 출력되는 값
    - <code>cur</code> current : 현재 돌고 있는 요소
    - <strong><code>initialValue</code> : acc의 초기값 <em>(optional)</em></strong></p>
<pre><code class="language-js">const numbers = [1, 2, 3, 4];
let sum = numbers.reduce((accumulator, current) =&gt; accumulator + current, 0);
console.log(sum);</code></pre>
<p>내가 가장 헷갈렸던 부분이 이 부분이다.</p>
<p><strong>초기값을 할당할 경우 배열의 인덱스는 할당된 초기값이 인덱스 0번이 되어 돌아간다.</strong></p>
<pre><code class="language-js">acc += cur; // 0 += 1;
acc += cur; // 1 += 2;
acc += cur; // 3 += 3;
acc += cur; // 6 += 4;

console.log(acc); // 10</code></pre>
<p>하나 더 예시를 들어보자면, 다음과 같이 초기값 <strong><code>initialValue</code></strong> 를 10으로 할당했을 때, 동작은</p>
<pre><code class="language-js">const numbers = [1, 2, 3, 4];
let sum = numbers.reduce((accumulator, current) =&gt; accumulator + current, 10);
console.log(sum);</code></pre>
<pre><code class="language-js">acc += cur; // 10 += 1;
acc += cur; // 11 += 2;
acc += cur; // 13 += 3;
acc += cur; // 16 += 4;

console.log(acc); // 20</code></pre>
<p>위와 같이 되는 것이다.</p>
<h3 id="3-두-개의-인자-추가하기">3. 두 개의 인자 추가하기</h3>
<p>4개의 인자 + 1개의 초기값
    - <code>acc</code> accumulator : 누산기, 누적되는 값, 최종적으로 출력되는 값
    - <code>cur</code> current : 현재 돌고 있는 요소
    - <code>idx</code> index : 배열 요소의 순서 <em>(optional)</em>
    - <code>arr</code> array (또는 <code>src</code> source) : 현재 배열, 원본 배열 <em>(optional)</em>
    - <code>initialValue</code> : acc의 초기값 <em>(optional)</em></p>
<p>다음은 평균을 구하는 reduce 함수이다.</p>
<p>마지막에만 나누기를 위해서 index와 arr를 불러왔다.</p>
<pre><code class="language-jsx">const avg = numbers.reduce((acc, cur, index, arr) =&gt; {
    if (index === arr.length - 1) { // index가 마지막일 때
        return (acc + cur) / arr.length; // cur - 4
    }
    return acc + cur; // cur - 1, 2, 3
    }, 0);
ㅤ
console.log(&quot;avg&quot;, avg);</code></pre>
<h2 id="👉-해당-문제-풀이-보러가기">👉 해당 문제 풀이 보러가기</h2>
<p><a href="https://github.com/sooyv/Programmers-Algorithm-JS/commit/fddf8341a41fead185b3d0e48e45312efad4f94e">https://github.com/sooyv/Programmers-Algorithm-JS/commit/fddf8341a41fead185b3d0e48e45312efad4f94e</a></p>
<h2 id="소감">소감</h2>
<p>꽤 자주 사용하지만 매번 헷갈렸던 메소드를 정확히 알게 되어 가려운 부분을 확실히 긁은 느낌이다. 이번 기회로 기초를 단단히 해놓을 수 있어 다행이라고 생각한다. 앞으로도 빠르게 대충 아는 것보다 하나를 알아도 확실하게 알고자하며 공부를 해야겠다.✊</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[javascript - every()]]></title>
            <link>https://velog.io/@so_oyv/javascript-every</link>
            <guid>https://velog.io/@so_oyv/javascript-every</guid>
            <pubDate>Thu, 15 Jun 2023 06:43:08 GMT</pubDate>
            <description><![CDATA[<p><strong>Array.prototype.every()</strong></p>
<p>오늘도 역시 프로그래머스 문제 풀이 중 알게 된 <strong><code>every()</code></strong> 함수에 대해 알아보기로 했다.</p>
<blockquote>
<p><strong><code>.every()</code></strong> every() 메서드는 배열 안의 모든 요소가 주어진 판별 함수를 통과하는지 테스트합니다. Boolean 값을 반환합니다.</p>
</blockquote>
<p>즉, 배열의 모든 원소가 주어진 조건을 만족하는지 판별해 모두 만족한다면 ture를, 하나라도 만족하지 않는다면 false를 return 한다. </p>
<h2 id="구문">구문</h2>
<pre><code class="language-js">// 화살표 함수
every((element) =&gt; { ... } )
every((element, index) =&gt; { ... } )
every((element, index, array) =&gt; { ... } )

// 콜백 함수
every(callbackFn)
every(callbackFn, thisArg)

// 인라인 콜백 함수
every(function callbackFn(element) { ... })
every(function callbackFn(element, index) { ... })
every(function callbackFn(element, index, array){ ... })
every(function callbackFn(element, index, array) { ... }, thisArg)
</code></pre>
<h2 id="매개변수">매개변수</h2>
<p><strong><code>callbackFn</code></strong>
각 요소를 시험할 함수. 다음 세 가지 인수를 받습니다.</p>
<p><strong><code>element</code></strong>
배열에서 처리되는 현재 요소</p>
<p><strong><code>index</code></strong>
처리할 현재 요소의 인덱스</p>
<p><strong><code>array</code></strong>
every를 호출한 배열</p>
<p><strong><code>thisArg Optional</code></strong>
callbackFn을 실행할 때 this로 사용하는 값.</p>
<h2 id="반환값">반환값</h2>
<p><code>callbackFn</code>이 모든 배열 요소에 대해 참(truthy)인 값을 반환하는 경우 <code>true</code>, 그 외엔 <code>false</code> 를 반환합니다.</p>
<h2 id="예제">예제</h2>
<p>배열 요소의 크기 테스트</p>
<pre><code class="language-js">let arr1 = [1, 2, 3, 4, 5];

let result = arr1.every((e) =&gt; e &gt; 2);
console.log(result);     // false
// 배열의 모든 요소가 2보다 크다는 조건에 만족하지 않았기 때문에 false 반환


let arr2 = [5, 6, 7, 8, 9];
let result = arr2.every((e) =&gt; e &gt; 4);
console.log(result);    // true 
// 배열의 모든 요소가 4보다 크다는 조건에 만족하기 때문에 true 반환</code></pre>
<hr>
<h2 id="👉-해당-문제-풀이-보러가기">👉 해당 문제 풀이 보러가기</h2>
<p><a href="https://github.com/sooyv/Programmers-Algorithm-JS/commit/b4f4a80034978a4ae5dff284297ac69fe9389348">every() 함수 문제풀이 보러가기 - programmers-js-github</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[javascript - fill()]]></title>
            <link>https://velog.io/@so_oyv/javascript-fill</link>
            <guid>https://velog.io/@so_oyv/javascript-fill</guid>
            <pubDate>Wed, 14 Jun 2023 07:06:01 GMT</pubDate>
            <description><![CDATA[<p><strong>Array.prototype.fill()</strong></p>
<p>프로그래머스 문제 중 배열의 원소만큼 추가하기를 풀다가 다음과 같은 풀이를 보게 되었다.</p>
<pre><code class="language-jsx">function solution(arr) {
    return arr.reduce((list, num) =&gt; [...list, ...new Array(num).fill(num)], []);
}</code></pre>
<p>이 중 fill()함수에 대해서 알아보고, 이 코드에서 fill()함수가 어떻게 동작하는지 알기 위해서 정리해보고자 한다.</p>
<p>MDN 설명에 따르면</p>
<p><strong><code>fill()</code></strong> 메서드는 배열의 시작 인덱스부터 끝 인덱스의 이전까지 정적인 값 하나로 채우는 메서드이다.</p>
<pre><code class="language-jsx">const array1 = [1, 2, 3, 4];

// Fill with 0 from position 2 until position 4
console.log(array1.fill(0, 2, 4));
// Expected output: Array [1, 2, 0, 0]

// Fill with 5 from position 1
console.log(array1.fill(5, 1));
// Expected output: Array [1, 5, 5, 5]

console.log(array1.fill(6));
// Expected output: Array [6, 6, 6, 6]</code></pre>
<p><a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/fill">Array.prototype.fill() - JavaScript | MDN</a></p>
<h2 id="구문">구문</h2>
<pre><code class="language-jsx">arr.fill(value[, start[, end]])</code></pre>
<h2 id="매개변수">매개변수</h2>
<aside>

<p><strong>value</strong></p>
<p>배열에 채울 값을 지정</p>
<p><strong>start</strong></p>
<p>value 값을 채울 배열의 시작 index</p>
<p>입력하지 않으면 기본값은 0.</p>
<p><strong>end</strong></p>
<p>value 값을 채울 배열의 종료 index</p>
<p>입력하지 않으면 기본값은 배열의 길이(arr.length)이다.</p>
</aside>



<h2 id="예제">예제</h2>
<pre><code>arr.fill(’L’, 1, 3)</code></pre><p><img src="https://velog.velcdn.com/images/so_oyv/post/477bab2c-89bb-47ac-bd25-a8f40dd67f55/image.png" alt=""></p>
<blockquote>
<p><strong><code>fill()</code></strong> 메서드는 배열의 start index부터 end index 전까지<strong>(end index는 미포함)</strong> value값으로 채워주는 함수입니다.</p>
</blockquote>
<h2 id="index가-음수일-때">index()가 음수일 때</h2>
<p>start나 end index가 음수로 지정되면 배열의 마지막 원소의 index가 -1이 되고,앞으로 올수록 인덱스가 감소한다.</p>
<pre><code>arr.fill(’L’, -3, -1)</code></pre><p><img src="https://velog.velcdn.com/images/so_oyv/post/4e31c615-9ffb-483e-98b5-95992487d47b/image.png" alt=""></p>
<h2 id="fill을-이용한-배열-초기화">fill()을 이용한 배열 초기화</h2>
<p><strong>const arr = new Array(5).fill(&#39;A&#39;);</strong></p>
<p>new Array() 구문을 사용하여 배열을 생성하면,</p>
<p>5개의 element를 가지는 배열이 생성되고,</p>
<p>각 element의 값은 undefined입니다.</p>
<p>여기에 fill() 함수를 사용하면, 생성된 배열의 element의 초기값을 지정할 수 있다. </p>
<h1 id="👉-해당-문제-풀이-보러가기">👉 해당 문제 풀이 보러가기</h1>
<p><a href="https://github.com/sooyv/Programmers-Algorithm-JS/commit/7a620cd778abc8fe1685066def96cd6ae717d111">https://github.com/sooyv/Programmers-Algorithm-JS/commit/7a620cd778abc8fe1685066def96cd6ae717d111</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Linux] 리눅스 기초 명령어]]></title>
            <link>https://velog.io/@so_oyv/Linux-%EB%A6%AC%EB%88%85%EC%8A%A4-%EA%B8%B0%EC%B4%88-%EB%AA%85%EB%A0%B9%EC%96%B4</link>
            <guid>https://velog.io/@so_oyv/Linux-%EB%A6%AC%EB%88%85%EC%8A%A4-%EA%B8%B0%EC%B4%88-%EB%AA%85%EB%A0%B9%EC%96%B4</guid>
            <pubDate>Fri, 06 Jan 2023 13:49:14 GMT</pubDate>
            <description><![CDATA[<h3 id="명령-프롬프트">명령 프롬프트</h3>
<p>$: 사용자
#: 관리자</p>
<h3 id="1-ls-명령어">1. ls 명령어</h3>
<p>List
현재 위치의 파일/디렉터리 목록을 출력</p>
<p>ls -a : 숨김파일도 전부 출력
ls -l : 상세출력
ls -la : 숨김파일도 전부 상세출력</p>
<h3 id="2-cd-명령어">2. cd 명령어</h3>
<p>Change Directory</p>
<p>경로 이동 문자
. : 현재 경로
.. : 이전 경로</p>
<p>~ : 현재 로그인한 사용자의 Home 디렉터리
/ : 최상위 디렉터리</p>
<p>홈 디렉터리로 이동
==&gt; cd ~</p>
<p>경로이동방법
상대경로 : 현재 내가 있는 위치를 기준으로 이동
절대경로 : [/]를 기준으로 이동</p>
<p>ex) 현재 내가 /home/so.oyv/test 폴더 안에 있다.
home 폴더로 이동하는 절대경로 방법과 상대경로 방법은?
cd /home --&gt; 절대경로
cd ../..      --&gt; 상대경로</p>
<h3 id="3-mkdir">3. mkdir</h3>
<p>make directory
새로운 폴더 생성하기</p>
<h3 id="4-rmdir">4. rmdir</h3>
<p>remove directory</p>
<ul>
<li>rmdir은 실제 사용하지 않음
문제점 : 폴더 안에 다른 파일이 있으면 삭제불가</li>
</ul>
<h3 id="5-rm">5. rm</h3>
<p>Remove
삭제 명령어
디렉터리든, 파일이든 어떤 파일도 삭제가 가능
rm -r 명령어를 사용해도 삭제가 가능</p>
<h3 id="6-cat-명령어">6. cat 명령어</h3>
<p>concatenate
파일의 글[코드]를 읽을 때 사용
ex) cat /etc/passwd</p>
<h3 id="7-vi-명령어----이-외에도-vim-혹은-그래픽모드면-gedit-등-다양한-메모장-프로그램이-있다">7. vi 명령어 --&gt; 이 외에도 vim 혹은 그래픽모드면 gedit 등 다양한 메모장 프로그램이 있다.</h3>
<p>윈도우로 생각하면 메모장
종료 방법 : [:] 입력 후 [q] [Enter]</p>
<h3 id="8-head-와-tail">8. head 와 tail</h3>
<p>기본 10줄을 출력한다
head : 위에서 10줄
tail : 아래에서 10줄
만약 5줄만 출력하고 싶다면?
-head -5</p>
<h3 id="9-more">9. more</h3>
<p>많은 글씨가 있으면, 페이지 단위로 끊어서 보여준다
페이지 앞으로 이동 : [Space Bar]
페이지 뒤로 이동 : [b]</p>
<h3 id="10-touch">10. touch</h3>
<p>0kb 파일을 생성하거나, 파일이 존재하면 수정시간 변경</p>
<h3 id="11-mv">11. mv</h3>
<p>move + 이름바꾸기</p>
<p>mv [원본파일] [이동 및 바꿀 파일]</p>
<h3 id="12-cp">12. cp</h3>
<p>파일을 복사한다</p>
<h3 id="13-su">13. su</h3>
<p>switch user
유저를 변경한다.
su 만 치면 root계정으로 접속</p>
<h3 id="14-sudo">14. sudo</h3>
<p>관리자권한
super user do</p>
<h3 id="15-op-code--operand">15. Op Code // Operand</h3>
<p>int a = 30;
mv /home/kitri/test.txt /tmp/abcd.txt</p>
<p>OP code = 명령어
Operand = 주소</p>
<p>mv    /home/so.oyv/test    /tmp/abcd</p>
<p>touch /home/so.oyv/test</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[spring] MVC와 템플릿 엔진]]></title>
            <link>https://velog.io/@so_oyv/spring-MVC%EC%99%80-%ED%85%9C%ED%94%8C%EB%A6%BF-%EC%97%94%EC%A7%84</link>
            <guid>https://velog.io/@so_oyv/spring-MVC%EC%99%80-%ED%85%9C%ED%94%8C%EB%A6%BF-%EC%97%94%EC%A7%84</guid>
            <pubDate>Thu, 05 Jan 2023 13:40:12 GMT</pubDate>
            <description><![CDATA[<h2 id="mvc">MVC?</h2>
<p>mvc란 Model, View, Controller의 약자이다.</p>
<h2 id="작동-원리">작동 원리</h2>
<p><img src="https://velog.velcdn.com/images/so_oyv/post/51c16bd5-8056-4a48-97ae-9329001a60f0/image.png" alt="">
url에서 name=spring!! 을 받았다.</p>
<h3 id="1-controller">1. Controller?</h3>
<p><img src="https://velog.velcdn.com/images/so_oyv/post/2a70c683-89ec-4b13-8b34-229bf95a54f8/image.png" alt="">
Controller의 @RequestParam 어노테이션으로 파라미터로 넘어온 &quot;name&quot;을 가져오고, String name은 spring!!이 된다. 이렇게 가져 온 데이터는 model이라는 변수에 addAtrribute에 담아준다.</p>
<pre><code>return &quot;hello-template&quot;;</code></pre><p>hello-template.html과 매핑된다</p>
<h3 id="2-view">2. View</h3>
<p><img src="https://velog.velcdn.com/images/so_oyv/post/a8887e7b-2ac7-46c8-a93d-874f1b2720a2/image.png" alt=""></p>
<p>hello-template.html으로 model이 전달되고, ${name}에는 key : name 의 value 값인 spring!!이 전달된다.</p>
<h3 id="✅-정리">✅ 정리!</h3>
<p><img src="https://velog.velcdn.com/images/so_oyv/post/e1b7c5e3-8a7a-4834-852f-ba4bf07f96c6/image.png" alt="">
웹브라우저에서 localhost:8080/hello-mvc를 넘기면 스프링 부트가 띄울때 같이 띄워지는 내장 톰켓 서버를 먼저 거치게 된다.
내장 톰켓 서버는 hello-mvc를 스프링에 던진다.
스프링에서는 Controller에 매핑되어 있는지 찾은 뒤, 매핑되어 있는 메서드를 호출해준다. 리턴시 hello-template으로 하고, key:name, value:spring!! 인 model을 스프링에게 넘겨준다.
스프링은 viewResolver가 view를 찾아주고 템플릿 엔진과 연결시켜준다. viewResolver는 return: hello-template과 똑같은 templates/hello-spring.html을 찾아서 thymeleaf 템플릿 엔진에게 처리를 넘긴다.
템플릿 엔진은 랜더링 후 변환을 한 html을 웹브라우저에 반환한다.</p>
<p>정적일때는 변환하지 않고 그대로 넘겨준다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Javascript] 객체(object)]]></title>
            <link>https://velog.io/@so_oyv/Javascript-%EA%B0%9D%EC%B2%B4object</link>
            <guid>https://velog.io/@so_oyv/Javascript-%EA%B0%9D%EC%B2%B4object</guid>
            <pubDate>Wed, 02 Nov 2022 13:40:31 GMT</pubDate>
            <description><![CDATA[<h1 id="1-객체">1. 객체</h1>
<p>자바스크립트의 여덟가지 자료형 중 일곱개는 오직 하나의 데이터(string, number...)만을 담을 수 있어 &#39;원시형(primitive type)&#39;이라고 부른다. 
하지만 객체형은 다양한 데이터를 담을 수 있다. </p>
<h1 id="2-객체의-생성">2. 객체의 생성</h1>
<p>객체는 중괄호{}를 이용해 만든다. 중괄호 안에는 &#39;키(key):값(value)&#39;쌍으로 구성된 프로퍼티(property)를 여러 개 넣을 수 있다. 이때 키(key)에는 문자형, 값(value)에는 모든 자료형이 허용된다.</p>
<p>비어있는 객체를 만드는 방법은 두 가지가 있다. </p>
<pre><code>1 let user = new object(); // &#39;객체 생성자&#39;
2 let user = {};           // &#39;객체 리터럴&#39;</code></pre><p>이때 1번은 &#39;객체 생성자&#39; 문법이고 2번은 &#39;객체 리터럴&#39; 문법이라고 한다.</p>
<h1 id="3-리터럴과-프로퍼티">3. 리터럴과 프로퍼티</h1>
<p>중괄호{} 안에는 &#39;키:값&#39;의 쌍으로 구성된 프로퍼티가 들어간다.</p>
<pre><code>let user = {
    name: &quot;chloe&quot;,       //키: &quot;name&quot;, 값: &quot;chloe&quot;
    age: 24,             //키: &quot;age&quot;, 값: 24
    sex: female            //키: &quot;sex&quot;, 값: &quot;female&quot;
};</code></pre><p>이렇게 되면, 객체 user에는 세 개의 프로퍼티가 생성된 것이다.</p>
<ol>
<li>&quot;name&quot; 과 &quot;chloe&quot;</li>
<li>&quot;age&quot; 와 24</li>
<li>&quot;sex&quot; 와 &quot;female&quot;</li>
</ol>
<p><img src="https://velog.velcdn.com/images/so_oyv/post/61594f79-5d60-4389-844e-6cfc8096367c/image.jpeg" alt=""></p>
<p>쉽게 생각하자면, user라는 이름을 가진 서랍 안에, name, age, sex라는 세 개의 파일(프로퍼티)이 담겨있는 것이라고 생각하면 된다.</p>
<h1 id="4-프로퍼티-추가와-삭제">4. 프로퍼티 추가와 삭제</h1>
<h3 id="프로퍼티-추가">프로퍼티 추가</h3>
<pre><code>user. isAdmin = ture;</code></pre><p><img src="https://velog.velcdn.com/images/so_oyv/post/b29c3b3f-b3cc-4b03-89b8-500a1bdf2b35/image.jpeg" alt=""></p>
<p>프로퍼티 값에는 모든 자료형이 올 수 있다. 불린형 프로퍼티를 추가해 보았다.</p>
<h3 id="프로퍼티-삭제">프로퍼티 삭제</h3>
<pre><code>delete user.sex;</code></pre><p><img src="https://velog.velcdn.com/images/so_oyv/post/c4c59843-7311-4933-81ab-4af8cd169324/image.jpeg" alt=""></p>
<p>delete 연산자를 사용하면 프로퍼티를 삭제할 수 있다.</p>
<h1 id="5-값-표기법">5. 값 표기법</h1>
<p>프로퍼티 키를 만든 다음엔 점 표기법 또는 대괄호 표기법을 프로퍼티에 접근해 값을 읽을 수 있다.
다음은 각각 user 객체의 name에 접근하는 표기법이다.</p>
<h3 id="5-1-점-표기법">5-1. 점(.) 표기법</h3>
<pre><code>user.name        // &quot;chloe&quot;</code></pre><p>점 표기법은 키가 &#39;유효한 변수 식별자’인 경우에만 사용할 수 있다. 공백이 없고, 숫자로 시작하지 않아야하며, $와 _를 제외한 특수 문자가 없어야 한다.</p>
<h3 id="5-2-대괄호-표기법square-bracket-notation">5-2. 대괄호([]) 표기법(square bracket notation)</h3>
<pre><code>user[&quot;name&quot;]</code></pre><p>유효한 변수 식별자가 아닌 경우에는 대괄호 표기법을 사용한다.
대괄호 표기법을 사용할 때에는 &#39; &#39;또는 &quot; &quot; 따옴표로 묶어 사용해야 한다. 이 경우 여러 단어를 조합해 만든 공백이 있는 프로퍼티를 불러올 수 있다는 장점이 있다.</p>
<p>예를 들어, user라는 객체에 &quot;show vidio&quot;라는 프로퍼티가 있다고 해보자.</p>
<pre><code>user.show vidio</code></pre><p>점 표기법을 사용해 작성하면, 자바스크립트는 위와 같은 코드를 이해하지 못할 것이다. use.show 까지는 이해하더라도, 이후 공백 때문에 vidio는 syntax error가 발생한다.</p>
<pre><code>user[&#39;show vidio&#39;]</code></pre><p>이 경우 대괄호 표기법을 사용하는 것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[css 우선순위]]></title>
            <link>https://velog.io/@so_oyv/css-%EC%9A%B0%EC%84%A0%EC%88%9C%EC%9C%84</link>
            <guid>https://velog.io/@so_oyv/css-%EC%9A%B0%EC%84%A0%EC%88%9C%EC%9C%84</guid>
            <pubDate>Tue, 25 Oct 2022 12:39:02 GMT</pubDate>
            <description><![CDATA[<h2 id="css언어의-cascading">css언어의 Cascading</h2>
<p>css란 Cascading Style Sheets 의 약자로 이 중 중요한 것은 바로 Cascading이다. 스타일 시트에서 코드의 충돌이 일어나지 않도록 우선순위를 아는것은 중요하다. 우선순위를 알지 못하면 내가 원하는 대로 또는 예상한대로 코드가 나오지 않는 경우가 일어날 수 있다.</p>
<p>캐스케이딩의 우선순위를 결정하는 요인은 다음 세가지이다. </p>
<h2 id="cascading-우선순위">cascading 우선순위</h2>
<ol>
<li>Importance(중요도)</li>
<li>Specificity(명시도)</li>
<li>Source order(코드순서)</li>
</ol>
<h3 id="1-importancs중요도">1. Importancs(중요도)</h3>
<p>css가 어디에 선언 되었느냐에 따라 우선순위가 달라진다.</p>
<ul>
<li>head 요소 안의 style 요소</li>
<li>head 요소 안의 style 요소 안의 @import</li>
<li>&lt;link&gt; 로 연결된 CSS 파일</li>
<li>&lt;link&gt; 로 연결된 CSS 파일 내의 @import</li>
<li>브라우저 디폴트 스타일시트</li>
</ul>
<h3 id="2-specificity명시도">2. Specificity(명시도)</h3>
<p>태그 속성에 스타일을 줄 때 선택자를 사용한다. 이 선택자를 선언할때 선택자의 유형에 따라 가중치가 주어지고 우선순위가 결정된다. 대상을 명확하게 특정할수록 우선순위가 높아진다.</p>
<blockquote>
<p>** style &gt; id &gt; class, pseudo-element &gt; 태그**</p>
</blockquote>
<p>class 선택자와 id 선택자가 있을 경우 id 선택자가 우선된다. </p>
<pre><code>&lt;style&gt;
/* 1. specificity: 1-0-1 */
#outer a {
    background-color: red;
}

/* 2. specificity: 2-0-1 */
#outer #inner a {
    background-color: blue;
}
&lt;style&gt;

&lt;body&gt;
    &lt;div id=&quot;outer&quot; class=&quot;container&quot;&gt;
        &lt;div id=&quot;inner&quot; class=&quot;container&quot;&gt;
            &lt;ul&gt;
                &lt;li class=&quot;nav&quot;&gt;&lt;a href=&quot;#&quot;&gt;One&lt;/a&gt;&lt;/li&gt;
                &lt;li class=&quot;nav&quot;&gt;&lt;a href=&quot;#&quot;&gt;Two&lt;/a&gt;&lt;/li&gt;
            &lt;/ul&gt;
        &lt;/div&gt;
    &lt;/div&gt; 
&lt;body&gt;</code></pre><p>위의 경우 첫번째에는 id 1개, class 0개, element 1개이지만, 두번째 코드에서 id 선택자가 2개로 우선순위가 정해졌다.
따라서 background-color는 blue가 된다. </p>
<blockquote>
<p>** !important &gt; style &gt; id &gt; class, pseudo-element &gt; 태그**</p>
</blockquote>
<p>이 모든 것을 무효화 하는 것이 <strong>!important</strong> 이다. 
이것은 cascading의 우선 순위를 무시한다. 하지만 남용하지 말고 주의해서 사용해야한다.</p>
<h3 id="3-source-order코드순서">3. Source order(코드순서)</h3>
<p>동일한 가중치를 갖는 요소가 두개 이상인 경우, 마지막에 선언된 코드가 우선된다. 이는 앞에 쓰인 코드가 뒤에 쓰인 코드에 의해 덮여쓰인다고 할 수 있다. </p>
<pre><code>&lt;style&gt;
 h1 {
   color: red;
 }

h1 {
   color:blue;
}
&lt;/style&gt;


&lt;div&gt;
  &lt;h1&gt;Hello&lt;/h1&gt;
&lt;/div&gt;</code></pre><p>위의 경우 h1 태그에 적용되는 색상은 blue이다.</p>
<p>참고
<a href="https://developer.mozilla.org/ko/docs/Learn/CSS/Building_blocks/Cascade_and_inheritance#%EA%B3%84%EB%8B%A8%EC%8B%9D_cascade_%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0">MDN - Cascade and inheritance</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[기본 키(Primary key)와 외래 키(Foreign key), 후보키]]></title>
            <link>https://velog.io/@so_oyv/%EA%B8%B0%EB%B3%B8-%ED%82%A4Primary-key%EC%99%80-%EC%99%B8%EB%9E%98-%ED%82%A4Foreign-key-%ED%9B%84%EB%B3%B4%ED%82%A4-%EC%8A%88%ED%8D%BC%ED%82%A4</link>
            <guid>https://velog.io/@so_oyv/%EA%B8%B0%EB%B3%B8-%ED%82%A4Primary-key%EC%99%80-%EC%99%B8%EB%9E%98-%ED%82%A4Foreign-key-%ED%9B%84%EB%B3%B4%ED%82%A4-%EC%8A%88%ED%8D%BC%ED%82%A4</guid>
            <pubDate>Sun, 23 Oct 2022 13:31:17 GMT</pubDate>
            <description><![CDATA[<p>저번 글에서는 데이터베이스에서 테이블간 &#39;관계&#39;와 그 관계에 따라 부모테이블과 자식테이블이 어떻게 정의되는지 정리하였다. 이번에는 기본 키(Primary key)와 외래 키(Foreign key)에 대해 정리해보자!</p>
<hr>
<h2 id="키key">키(Key)</h2>
<p>키는 무엇인가를 식별한다는 의미를 갖고 있다. 키란 데이터베이스의 관계에서 특정 튜플을 식별할 때 사용하는 속성 또는 속성의 집합을 말한다.</p>
<h2 id="후보키candidate-key">후보키(Candidate Key)</h2>
<p>후보키는 기본키가 될 수 있는 후보가 될 수 있는 모든 키를 의미한다. 기본키도 후보키에 속한다. 후보키 중 기본키로 지정되지 않은 키를 보조키라고 한다. </p>
<h2 id="기본-키primary-key와-외래-키foreign-key">기본 키(Primary key)와 외래 키(Foreign key)</h2>
<h3 id="기본-키primary-key--pk">기본 키(Primary key : PK)</h3>
<p> 관계형 데이터베이스에서 테이블 내의 식별자로 이용하기 가장 적합한 컬럼에 정의되는 키이다. 테이블에는 각 행을 고유하게 식별하는 값을 가진 열들이 있다. 이러한 열의 조합을 기본키(PK)라고 하며 테이블의 엔티티(Entity)의 무결성에 사용된다. 
 기본기 선정의 몇가지 조건은 다음과 같다.</p>
<ul>
<li>테이블은 오직 하나의 Primary key값을 가진다.</li>
<li>NULL값을 허용하지 않는다.</li>
<li>값의 중복이 없어야 한다.</li>
<li>키 값이 변하지 않아야 한다.</li>
<li>최대한 적은 수의 속성을 가진 것이 좋다.</li>
</ul>
<h3 id="외래-키foreign-key--fk">외래 키(Foreign key : FK)</h3>
<p> 외래 키는 두 테이블 간 연결을 설정한다. 즉, 어떤 테이블에 포함되어 있으면서, 다른 테이블에서 <strong>기본 키를 참조</strong>하여 두 테이블 간의 관계를 나타낸다. </p>
<ul>
<li>외래 키는 관계를 표현한다.</li>
<li>외래 키는 기본 키와 다르게 하나의 테이블에 여러 개를 지정 할 수 있다.</li>
<li>외래 키는 NULL값과 중복 값이 허용된다.</li>
<li>참조되는 값이 변경되면 참조하는 값도 변경된다.</li>
<li>외래 키는 기본 키의 일부가 될 수 있다.</li>
</ul>
<hr>
<p><img src="https://velog.velcdn.com/images/so_oyv/post/28d649d9-fe1c-4c2d-8173-2bb76e549f72/image.jpeg" alt=""></p>
<p>관계형 데이터베이스에서 두 테이블 간 관계를 연결하는 방법은 부모테이블의 기본 키(Primary key)와 자식테이블의 외래 키(Foreign key)이다.</p>
<p>위의 테이블을 보면 수강 테이블의 외래 키(PK)가 학생테이블의 주 키(Primary key) &#39;학번&#39;에 참조 되어 있는 것을 볼 수 있다. 
이것은 학생 테이블이 기본키인 학번이 수강 테이블의 외래키로 전이 되었다고 표현 할 수 있다.
학번은 각 학생의 고유한 값으로 중복 될 일이 없다. 만약 이름이 똑같은 학생이 있을 경우 유일성을 만족하지 못한다. 전공도 마찬가지로 중복의 가능성이 있으므로 Primary key의 조건에 위배된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[데이터베이스 '관계']]></title>
            <link>https://velog.io/@so_oyv/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EA%B4%80%EA%B3%84</link>
            <guid>https://velog.io/@so_oyv/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EA%B4%80%EA%B3%84</guid>
            <pubDate>Thu, 20 Oct 2022 15:22:16 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/so_oyv/post/2dc41492-49fb-4129-9aa1-7e9dc5c3420c/image.jpeg" alt=""></p>
<p>위의 테이블에는 학생테이블과 수강테이블이 있다. 하지만 학생 테이블의 정보와 수강테이블의 정보를 어떻게 연결할 것인가에 대한 문제점이 생긴다.
학생 테이블을 통해 학생의 정보를 알 수는 있지만, 수강 테이블의 정보가 어느 학생의 정보인지 알 수 없다는 것이다.</p>
<p>이와 같은 문제를 해결하기 위해서 <strong>&#39;관계&#39;</strong>라는 개념이 필요하다.</p>
<h2 id="1-관계란">1. 관계란?</h2>
<p>관계형데이터베이스에서의 관계란 테이블 간 <strong>업무적인 연관성</strong>을 뜻한다.
테이블을 한 업무로 생각하고, 테이블 간 업무적 연관성을 생각해보면 &#39;관계&#39;를 알 수 있는 것이다.</p>
<p>위의 학생테이블과 수강테이블의 연관성은 <strong>학생의 성적을 알기 위해 만들어진 것</strong>이다. 하지만 위의 테이블에서는 수강테이블에 저장된 01111A를 수강하는 학생이 누구인지 알 수 없는 상태이다. 이를 알기 위해서는 수강 테이블에 등록된 정보 중에는 적어도 하나 이상의 학생 정보를 가지고 있어야 한다. 
즉, 현재의 테이블에서는 학생과 수강을 연결할 수 있는 관계가 없기 때문이다.</p>
<h2 id="2-부모테이블과-자식테이블">2. 부모테이블과 자식테이블</h2>
<p>두 테이블 간에 관계가 만들어지며, 둘 중 하나의 테이블은 부모(parent)테이블이 되고 다른 하나는 자식(child)테이블이 된다. 
이것은 행위의 주체에 따라 구분이 된다.
학생과 수강 두 테이블을 보자면, 학생이 과목을 수강하는 것이기 때문에 학생 테이블이 부모테이블, 수강 테이블이 자식테이블이 된다. </p>
<h2 id="3-관계의-유형">3. 관계의 유형</h2>
<ul>
<li>존재의 유형</li>
<li>행위의 유형</li>
</ul>
<p>존재의 유형은 쉽게 소유의 유형이라고 생각하면 된다. 예를 들어 부서와 사원이 있다고 생각해보자. 부서에는 사원이 소속되어 있다. 이것은 어떤 행위에 의해 발생된 일이 아닌, 그냥 그 존재자체의 상태를 말하는 것이다.</p>
<p>행위의 유형은 고객과 주문을 생각해보자. 즉 고객이 주문이라는 행위에 의해 관계가 형성되는 것이다.</p>
<p>위의 테이블은 &#39;학생이 과목을 수강한다&#39;라고 정의 할 수 있다.
그러므로 행위의 유형이다.</p>
<h3 id="정리">정리</h3>
<ul>
<li>관계는 두 테이블 사이에 존재하는 업무적인 연관성으로, 행위의 주체에 따라 부모테이블과 자식테이블이 된다.</li>
<li>관계는 유형에 따라 존재의 유형 또는 행위의 유형으로 나눠진다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[데이터베이스]]></title>
            <link>https://velog.io/@so_oyv/Database</link>
            <guid>https://velog.io/@so_oyv/Database</guid>
            <pubDate>Sat, 15 Oct 2022 16:06:16 GMT</pubDate>
            <description><![CDATA[<h2 id="1-데이터베이스">1. 데이터베이스</h2>
<h3 id="1-1-데이터와-정보">1-1. 데이터와 정보</h3>
<p>데이터베이스를 알기에 앞서 정보란 무엇이고 데이터란 무엇일까? 이 둘은 어떻게 구분 할 수 있을까? 
그것은 바로 <strong>&#39;가공&#39;</strong>의 차이에 있다.
&#39;짱구, 철수, 유리, 10, 11, 10, 서울, 부산, 수원&#39;은 각각 있을때 데이터에 불과하다.</p>
<table>
<thead>
<tr>
<th align="center">이름</th>
<th align="center">연령</th>
<th align="center">주거지</th>
</tr>
</thead>
<tbody><tr>
<td align="center">짱구</td>
<td align="center">10</td>
<td align="center">서울</td>
</tr>
<tr>
<td align="center">철수</td>
<td align="center">11</td>
<td align="center">부산</td>
</tr>
<tr>
<td align="center">유리</td>
<td align="center">10</td>
<td align="center">수원</td>
</tr>
<tr>
<td align="center">하지만 위의 표처럼 데이터를 가공했을 때 정보라고 할 수 있다.</td>
<td align="center"></td>
<td align="center"></td>
</tr>
</tbody></table>
<h3 id="1-2-데이터베이스란">1-2. 데이터베이스란?</h3>
<p>데이터베이스는 자료의 모음으로 데이터를 체계화 ∙ 구조화하여 통합 관리하는 정보의 집합이다. 이렇게 정리된 데이터는 검색과 갱신의 효율을 높인다.</p>
<h3 id="1-3-데이터베이스-관리-시스템dbms">1-3. 데이터베이스 관리 시스템(DBMS)</h3>
<p>데이터베이스를 좀 더 효과적으로 운영하고 관리하고자 하는 목적으로 만들어진 것이 데이터베이스 관리 시스템(Database Managment system)이다.
다수의 사용자 또는 응용프로그램의 요구사항을 관리함으로써 그 데이터가 어디에 어떻게 저장되는지 몰라도 다중 사용자 환경에서 누구나 데이터를 이용할 수 있도록 해준다.</p>
<h3 id="관계형-데이터베이스-시스템rdbms">관계형 데이터베이스 시스템(RDBMS)</h3>
<p>데이터베이스는 테이블로 구성되며 그 테이블들은 컬럼으로 연결한 것이다. 
이때, 각 테이블은 낭비되는 공간 없이 효율적으로 데이터를 저장하도록 한다. 이렇게 나뉘어진 테이블들은 상위 정보를 저장하는 주 키(Primary key)와 하위 정보를 저장하는 테이블의 외래 키(Foreign key)를 이용하여 연결되고 각 테이블의 연관된 정보를 얻을 수 있도록 한다.</p>
<h3 id="1-4-sql">1-4. SQL</h3>
<p>위의 관계형 데이터베이스 관리 시스템(RDBMS)의 데이터를 관리하기 위해 설계된 것이 SQL이다. SQL은  SQL 구문을 통해 테이블 내의 데이터를 검색하거나 조작하는 등으로 사용된다. 그 구문은 다음과 같다.</p>
<h4 id="sql-구문의-종류">SQL 구문의 종류</h4>
<ul>
<li><p>데이터 정의 언어(DDL: Data Definition Language)
데이터를  생성(Create), 수정(Update), 삭제(Delete)하는 역할을 한다.</p>
</li>
<li><p>데이터 조작 언어(DML: Data Manipulation Language)
데이터베이스에 등록된 레코드를 조회(Select),수정(Update),삭제(Delete)하는 역할을 한다.</p>
</li>
<li><p>데이터 제어 언어(DCL: Data Control Language)
데이터의 사용, 접근에 대한 권한(Grant)을 주는 역할을 한다.</p>
</li>
<li><p>트렌젝션 제어 언어(TCL: Transaction Control Language)
트렌젝션을 제어할때 사용한다. </p>
</li>
</ul>
<h3 id="추가공부내용">추가공부내용!</h3>
<p>이후 추가 공부 내용</p>
<ul>
<li>테이블과 칼럼</li>
<li>주 키와 외래키</li>
<li>SQL구문의 종류</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>