<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>kyj-8754.log</title>
        <link>https://velog.io/</link>
        <description>메모리폼</description>
        <lastBuildDate>Wed, 07 May 2025 05:38:40 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>kyj-8754.log</title>
            <url>https://velog.velcdn.com/images/kyj-8754/profile/221e503c-050e-46cf-80f3-58eb19f87871/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. kyj-8754.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/kyj-8754" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Spring Boot 5월 1주차]]></title>
            <link>https://velog.io/@kyj-8754/Spring-Boot-5%EC%9B%94-1%EC%A3%BC%EC%B0%A8</link>
            <guid>https://velog.io/@kyj-8754/Spring-Boot-5%EC%9B%94-1%EC%A3%BC%EC%B0%A8</guid>
            <pubDate>Wed, 07 May 2025 05:38:40 GMT</pubDate>
            <description><![CDATA[<p><span style=color:red>※기본적으로 MVC패턴을 모두 인지했다는 가정하에 진행되는 리뷰이므로, 전에 배운 공통적인 부분은 넘어갈 예정이다.</span></p>
<h1 id="프로젝트-기본설정">프로젝트 기본설정</h1>
<p><a href="https://spring.io/tools">sts4 설치경로</a></p>
<p><a href="https://start.spring.io/">Boot 기본설정</a></p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/9f1c4d38-22c0-4899-be5d-8a962a3e02a9/image.png" alt="기본 설정"></p>
<p>Spring Legacy 개발환경에서 Boot로 넘어가기위해 다음과 같은 설정을 하도록 하자</p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/9970c4ca-a66b-4340-bc6f-87495916fd37/image.png" alt=""></p>
<h3 id="applicationproperties설정">application.properties설정</h3>
<p>해당가린 부분은 각자 데이터베이스의 유저 id와 pw를 적도록하자</p>
<pre><code class="language-java">plugins {
    id &#39;java&#39;
    id &#39;org.springframework.boot&#39; version &#39;3.1.3&#39;
    id &#39;io.spring.dependency-management&#39; version &#39;1.1.7&#39;
}

group = &#39;org.kosa&#39;
version = &#39;0.0.1&#39;

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation &#39;org.springframework.boot:spring-boot-starter-web&#39;
    testImplementation &#39;org.springframework.boot:spring-boot-starter-test&#39;
    testRuntimeOnly &#39;org.junit.platform:junit-platform-launcher&#39;
    // devtools 추가
    developmentOnly &#39;org.springframework.boot:spring-boot-devtools&#39;
    // lombok
    compileOnly &#39;org.projectlombok:lombok&#39;
    annotationProcessor &#39;org.projectlombok:lombok&#39;
    //타임리프 설정
    implementation &#39;org.springframework.boot:spring-boot-starter-thymeleaf&#39;
    implementation &#39;nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect&#39;
    //JPA설정
    implementation &#39;org.springframework.boot:spring-boot-starter-data-jpa&#39;

    // 마리아 DB 설정
    implementation &#39;org.mariadb.jdbc:mariadb-java-client:3.0.11&#39;

    //validate 설정
    implementation &#39;org.springframework.boot:spring-boot-starter-validation&#39; 
}

tasks.named(&#39;test&#39;) {
    useJUnitPlatform()
}
</code></pre>
<p>build.gradle설정</p>
<p>주석처리는 다음과 같으며, 현재 진행과제에 맞게 의존성을 추가했다. 기존에 추가되지 않은 의존성을 추가할때마다 <code>프로젝트 우클릭 -&gt; gradle -&gt; Refresh Gradle Project</code>를 잊지않아야 한다.</p>
<h3 id="devtools">DevTools</h3>
<h3 id="타임리프thymeleaf">타임리프(thymeleaf)</h3>
<h3 id="jpa">JPA</h3>
<h3 id="validate">validate</h3>
<p>lagacy Project와는 다르게 lombok을 이용한 생성자지정을 다음과 같이한다.</p>
<pre><code class="language-java">package org.kosa.sbb.question;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;

@Getter
@Setter
public class LombokEx {
    private final String hello;
    private final int lombok;
}

public LombokEx(String hello, int lombok) {
        super();
        this.hello = hello;
        this.lombok = lombok;
    }
</code></pre>
<p><code>@RequiredArgsConstructor</code>를 지정하지 않을 경우 생성자 지정을 별도의 코드를 통해 작성해야한다. 그러나 어노테이션을 붙일 경우 생성자 지정 코드를 삭제가능하다.</p>
<pre><code class="language-java">package org.kosa.sbb.question;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@RequiredArgsConstructor
public class LombokEx {
    private final String hello;
    private final int lombok;
}
</code></pre>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/43718fba-ebe1-4cc0-8714-254afc75315d/image.png" alt=""></p>
<p>현재 디렉터리에서 몇가지를 언급하고 넘어가고자 한다.</p>
<h3 id="templates-디렉터리">templates 디렉터리</h3>
<p>src/main/resource 디렉터리 하위 templates에는 자바코드를 삽입할 수 있는 html파일을 저장하는 장소이다.</p>
<h3 id="static-디렉터리">static 디렉터리</h3>
<p> static 디렉터리에서는 스타일시트(.css), 자바스크립트(.js), 이미지파일(.jpeg, .png)를 저장하는 장소이다.</p>
<h3 id="applicationproperties-파일">application.properties 파일</h3>
<p>프로젝트 환경을 설정하는 파일로, 환경변수, 데이터베이스 설정을 하는 장소이다.</p>
<h3 id="srctestjava">src/test/java</h3>
<p>서버를 실행하지 않은 상태에서 프로젝트의 기능을 테스트하는 코드를 작성하여 저장하는 공간, JUnit이나 기타 스프링 부트의 테스트 도구를 이용하여 테스트가 가능하다</p>
<h3 id="buildgradle">build.gradle</h3>
<p>환경파일을 그래들로 사용하는걸 의미하며, Ant, Maven과 비슷하다고 보면된다. 프로젝트에 필요한 플러그인과 라이브러리를 설치하기 위해 사용되며, 기존 legacy에서는 Maven을 이용하여 진행했던것을 인지하자.</p>
<h1 id="jpa로-데이터-베이스-사용하기">JPA로 데이터 베이스 사용하기</h1>
<p>Legacy는 Mybatis로 DAO에 sql.xml을 매핑하여 DB에 명령문을 날리도록 했다. 이번 boot에서는 ORM과 JPA를 이용하여 DB에 sql문을 직접 사용하지 않고 관리하려 한다.</p>
<h3 id="ormobject-relational-mapping이란">ORM(Object-Relational Mapping)이란?</h3>
<p>ORM은 객체와 관계형 데이터베이스 자동매핑을 의미하며 SQL을 사용하지 않고 데이터베이스를 관리할 수 있는 도구이다. 자바 클래스로 테이블을 만들어 관리 할 수 있으며, 쿼리문 역시 보낼 수 있다.</p>
<pre><code class="language-java">        Question q1 = new Question();
        q1.setSubject(&quot;sbb가 무엇인가요?&quot;);
        q1.setContent(&quot;sbb에 대해서 알고 싶습니다.&quot;);
        q1.setCreateDate(LocalDateTime.now());
        this.questionRepository.save(q1);

        Question q2 = new Question();
        q2.setSubject(&quot;스프링 부트 모델 질문입니다.&quot;);
        q2.setContent(&quot;id는 자동으로 생성되나요?&quot;);
        q2.setCreateDate(LocalDateTime.now());
        this.questionRepository.save(q2);</code></pre>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/66d24d00-7ac7-48ab-8f18-6fb55d00fb5c/image.png" alt=""></p>
<blockquote>
<p>다음 명령문은 SQL로 insert하는 명령문을 ORM으로 구현한 모습니다.</p>
</blockquote>
<p>위와같이 SQL문을 ORM으로 대체하면 SQL문을 모르더라도 DB관리가 가능한것을 알 수 있다. 또 다른 장점으로는 MariaDB와 Oracle은 SQL이 조금씩 다르지만 ORM은 자동으로 SQL을 만들기 때문에 별도의 차이를 둘 필요가 없다.</p>
<p>여기서 Question은 자바클래스로 데이터베이스의 테이블과 매핑되는 역할을 하는데 ORM에서는 엔티티라고 부른다</p>
<h3 id="jpajava-persistence-api란">JPA(Java Persistence API)란?</h3>
<p>ORM 표준 기술로서, SQL을 자동으로 매핑해주는 인터페이스 모음이다. 여기서 JPA는 전부 사용하는것이 아닌 일부분을 사용하는것을 인지하자.</p>
<pre><code class="language-java">spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.show_sql=true</code></pre>
<p>예를 들어 다음과 같은 하이버네이트를 사용하는 문구를 확인해보자.</p>
<ul>
<li>spring.jpa.hibernate.ddl-auto</li>
</ul>
<p>하이버네이트의 데이터정의어로
<img src="https://velog.velcdn.com/images/kyj-8754/post/650c82ce-353a-43dc-b159-16b25bdb13fd/image.png" alt=""></p>
<p>다음과 같은 속성이 적용된다. 개발환경에서는 update를 주로 사용하고, 운영 환경에서는 none 또는 validate를 주로 사용한다.</p>
<blockquote>
<p>spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.show_sql=true</p>
</blockquote>
<p>두가지의 구문은 프로그램 실행시 콘솔에서 sql을확인하게 해주는 구문이다 설정값을 주게되면 다음과 같은 sql문을 확인 할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/9e50fbb9-b0af-4ee9-8260-4ee8341bd59d/image.png" alt=""></p>
<h3 id="엔티티-속성-부여">엔티티 속성 부여</h3>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/18af2ae6-7fdd-40a1-a0e2-686a83f68a7b/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/364ecc19-9e2e-4acf-ae84-8ff511d76f67/image.png" alt=""></p>
<blockquote>
<p>코드 색상때문에 이미지로 붙였다...</p>
</blockquote>
<p>마리아DB에서 미리 테이블을 만들었고, Boot에서 두개의 엔티티를 다음과 같이 정의하였다. 각 엔티티의 속성에 부여된 어노테이션은 다음과 같다.</p>
<ul>
<li><p>@ID
기본키로 지정한다는 어노테이션, 데이터베이스의 PK제약과 같다고 보면된다.</p>
</li>
<li><p>@GeneratedValue
새로운 값이 저장될 때마다 1씩 증가하여 저장하도록 지정하는 어노테이션, 데이터베이스의 시퀀스와 같다고 보면된다. GenerattionType.IDENTITY는 해당 속성만 별도로 번호가 늘어나도록 만든 명령문이며, strategy를 통해 해당 속성에 추가되는 값에만 번호를 부여하게 만든다.</p>
</li>
<li><p>@Column
엔티티의 속성은 DB테이블의 컬럼이름과 같은데, 이때 세부설정을 주기 위하여 주는 어노테이션이다. 예시와 같이 length를 통해 열의 길이를 제한하거나, columnDefinition을 통해 TEXT속성을 부여하기도 한다.</p>
</li>
</ul>
<blockquote>
<p>💡엔티티의 속성이름과 테이블 열의 이름이 다른것이 있다 바로 Question에 createDate와 데이터베이스의 create_date인데 카멜 케이스형식의 이름은 소문자로 변경되고 단어가 언더바로 구분되기 때문에 같은 이름이된다. 만약 이것이 싫다면 어노테이션에 name을 별도로 지정도 가능하다.</p>
</blockquote>
<pre><code class="language-java">@Column(name = &quot;create_date&quot;)
private LocalDateTime createDate;</code></pre>
<p>엔티티의 속성을 테이블의 열로 인식시키고 싶지 않다면<code>@Transient</code> 어노테이션을 적용하면 클래스의 속성으로만 적용가능하다.</p>
<ul>
<li>@ManyToOne
다대일관계를 만드는 어노테이션으로, 서로의 엔티티를 묶어주는 DB의 외래키설정 방식이다. 반대의 방식인 @OneToMany역시 가능하다. 이때 하나의 값에 여러 값이 묶여 오므로 List형태로 받아오며, 매핑되는 엔티티 명을 지정해주는 것을 알 수있다.</li>
</ul>
<blockquote>
<p>💡여기서 cascade는 Answer와 연관된 Question에도 동작을 반영하겠다는 의미이며, CascadeType.REMOVE는 부모(Question)가 삭제될 경우 자식인 Answer의 값도 자동으로 삭제하는것을 의미한다.</p>
</blockquote>
<h1 id="repository">Repository</h1>
<p>우리는 Legacy에서 CRUD를 명령문을 DAO+sql.xml+mapper 로 설정하였으나, boot에서는 리포지터리하나로 관리가 가능하다.</p>
<pre><code class="language-java">package org.kosa.sbb.answer;

import java.util.List;

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

public interface AnswerRepository extends JpaRepository&lt;Answer, Integer&gt;{

    List&lt;Answer&gt; findByQuestionId(Integer id);
}
</code></pre>
<p>JpaRepository에는 JPA에서 제공하는 CRUD를 처리하는 메서드가 이미 내장되어있다. 우리는 여기서 추가적으로 받아올 매개변수와 함수를 설정 가능하다.</p>
<p>대표적으로 몇개의 명령문만 알아보자.</p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/21e1be51-d015-4553-b33c-ae54f9913eef/image.png" alt=""></p>
<ul>
<li>findAll()<pre><code class="language-java">      List&lt;Question&gt; all = this.questionRepository.findAll();
      assertEquals(34, all.size()); // 현재 데이터 테이블의 데이터가 34개 들어있음</code></pre>
</li>
</ul>
<p>위 코드는 데이터베이스에 Question 테이블에 34개의 데이터가 있는지 확인하는 Junit 테스트 코드이다.</p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/6760d5ee-d1d5-41f6-ac5e-f3a385df9c0d/image.png" alt=""></p>
<blockquote>
<p>Run as -&gt; Junit test를 사용하면 위와같은 결과가 나오는것을 알 수 있다. </p>
</blockquote>
<p>💡assert는 JUnit 테스트에서 기대하는 결과와 실제 결과가 일치하는지를 검증하기 위한 핵심 도구이다. 여기서 assert는 말하는 건 정확히는 JUnit의 Assertions 클래스에서 제공하는 다양한 검증 메서드인것을 인지하자.</p>
<table>
<thead>
<tr>
<th>메서드</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>assertEquals(expected, actual)</code></td>
<td>기대 값과 실제 값이 같은지</td>
</tr>
<tr>
<td><code>assertNotEquals(unexpected, actual)</code></td>
<td>같지 않은지</td>
</tr>
<tr>
<td><code>assertTrue(condition)</code></td>
<td>조건이 true인지</td>
</tr>
<tr>
<td><code>assertFalse(condition)</code></td>
<td>조건이 false인지</td>
</tr>
<tr>
<td><code>assertNull(object)</code></td>
<td>객체가 null인지</td>
</tr>
<tr>
<td><code>assertNotNull(object)</code></td>
<td>객체가 null이 아닌지</td>
</tr>
<tr>
<td><code>assertThrows(Exception.class, () -&gt; ...)</code></td>
<td>예외가 발생하는지 검증</td>
</tr>
</tbody></table>
<ul>
<li>findBy속성명() </li>
</ul>
<pre><code class="language-java">    Optional&lt;Question&gt; oq = questionRepository.findById(32); // 아이디 번호가 32인걸 찾기
        if (oq.isPresent()) {
            Question q1 = oq.get();
            assertEquals(&quot;sbb가 무엇인가요?&quot;, q1.getSubject());
        }

        // 제목이 &quot;sbb가 무엇인가요?&quot;인걸 찾기, 대신에 무조건 1개만 찾아야 오류가 안뜬다.
        Question q = questionRepository.findBySubject(&quot;sbb가 무엇인가요?&quot;);
        if (q != null) {
            assertEquals(32, q.getId());
            assertEquals(&quot;sbb가 무엇인가요?&quot;, q.getSubject());
        }</code></pre>
<p>findById 또는 Subject를사용하여 값을 받아오는 함수</p>
<p>💡<code>Optional&lt;T&gt;</code>를 사용하는것을 볼 수 있는데. 이렇게 하면 데이터가 null일때도  NullPointException이 발생하지 않는다. </p>
<table>
<thead>
<tr>
<th>메서드</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>Optional.of(value)</code></td>
<td>절대 null 아님. null이면 예외 발생</td>
</tr>
<tr>
<td><code>Optional.ofNullable(value)</code></td>
<td>null 허용</td>
</tr>
<tr>
<td><code>Optional.empty()</code></td>
<td>비어 있는 Optional 객체 반환</td>
</tr>
<tr>
<td><code>isPresent()</code></td>
<td>값이 있는지 여부 (true/false)</td>
</tr>
<tr>
<td><code>ifPresent(consumer)</code></td>
<td>값이 있으면 실행</td>
</tr>
<tr>
<td><code>orElse(default)</code></td>
<td>값이 없으면 기본값 반환</td>
</tr>
<tr>
<td><code>orElseGet(Supplier)</code></td>
<td>지연 계산을 통해 기본값 반환</td>
</tr>
<tr>
<td><code>orElseThrow()</code></td>
<td>값이 없으면 예외 발생</td>
</tr>
</tbody></table>
<ul>
<li>findBySubjectAndContent()
위 코드 예시처럼 id와 subject 두개를 이용해 where절을 사용하고싶으면 다음위와같이 사용할 수 있다.</li>
</ul>
<p>아래 표는 AND구문 외에 다른 구문도 보여주고 있다</p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/7322c4bb-85e7-4304-8298-4918090ffef2/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CMD를 자바코드로 구현해보자.]]></title>
            <link>https://velog.io/@kyj-8754/CMD%EB%A5%BC-%EC%9E%90%EB%B0%94%EC%BD%94%EB%93%9C%EB%A1%9C-%EA%B5%AC%ED%98%84%ED%95%B4%EB%B3%B4%EC%9E%90</link>
            <guid>https://velog.io/@kyj-8754/CMD%EB%A5%BC-%EC%9E%90%EB%B0%94%EC%BD%94%EB%93%9C%EB%A1%9C-%EA%B5%AC%ED%98%84%ED%95%B4%EB%B3%B4%EC%9E%90</guid>
            <pubDate>Sun, 23 Mar 2025 09:25:55 GMT</pubDate>
            <description><![CDATA[<h1 id="코드">코드</h1>
<pre><code class="language-java">package project.cmd;

import java.io.*;
import java.text.*;
import java.util.*;

public class FileApp {

    private static String path = &quot;c:/&quot;;
    private static Scanner sc = new Scanner(System.in);


    // 파일 확인
    private static boolean validFile(File file, String dir) {
        return  file.exists() &amp;&amp; file.isFile() &amp;&amp; file.getName().equalsIgnoreCase(dir);
    }
    // 폴더 확인
    private static boolean validFolder(File file, String dir) {
        return  file.exists() &amp;&amp; file.isDirectory() &amp;&amp; file.getName().equalsIgnoreCase(dir);
    }
    // 디렉토리 확인
    private static boolean validDirectory(File file, String dir) {
        return file.exists() &amp;&amp; file.getName().equalsIgnoreCase(dir);
    }
    // 읽기 확인
    private static boolean validRead(File file) {
        return file.exists() &amp;&amp; file.canRead();
    }


    // 디렉토리 파일 출력 명령문
    private static void dir_cmd() {
        SimpleDateFormat sdf = new SimpleDateFormat(&quot;yyyy-MM-dd a HH:mm&quot;);
        DecimalFormat df = new DecimalFormat(&quot;#,###&quot;);

        File temp = new File(path);
        File[] contents = temp.listFiles();


        long total = 0;
        System.out.println(&quot;경로 &quot; + path);
        System.out.println();
        for (File file : contents) {
            System.out.printf(&quot;%-25s&quot;, sdf.format(new Date(file.lastModified())));
            if (file.isDirectory()) {
                System.out.printf(&quot;%-10s%-10s%-20s&quot;, &quot;&lt;DIR&gt;&quot;, &quot;&quot;, file.getName());
            } else {
                System.out.printf(&quot;%-10s%-10s%-20s&quot;, &quot;&quot;, df.format(file.length()), file.getName());
                total += file.length();
            }
            System.out.println();
        }
        System.out.printf(&quot;       %d개 파일   %s 바이트 \n&quot;, contents.length, df.format(total));
    }


    // 전체 파일 출력명령문
    private static void dirAll_cmd() {
        File temp = new File(path);
        File[] contents = temp.listFiles();

        dir_cmd();
         if (contents != null) {
            for (File file : contents) {
                if (file.isDirectory()) {
                    path = file.getPath();
                    dir_cmd();
                }
                path = file.getParent();
            }
         }
    }


    // 디렉토리 이동 명령문
    private static void cd_cmd(String[] arg) {
        if (arg.length != 2) {
            System.out.println(&quot;사용법 : cd 이동할 경로&quot;);
            return;
        }
        File file = new File(path + arg[1]);
        if (validDirectory(file,arg[1])) {
            path += arg[1];
        } else {
            System.out.println(arg[0] + &quot;&gt; 해당 폴더는 존재하지 않습니다.&quot;);
        }
    }

    // 이름 다시 짓기 명령문
    private static void rename_cmd(String[] arg) {
        if (arg.length != 3) {
            System.out.println(&quot;사용법 : rename 기존 파일명 변경할 파일명&quot;);
            return;
        }

        File file = new File(path +&quot;/&quot;+ arg[1]);
        if (validFile(file,arg[1]) || validFolder(file,arg[1])) {
            file.renameTo(new File(path +&quot;/&quot;+arg[2]));
        } else {
            System.out.println(arg[1] + &quot;이름의 해당 파일은 존재하지 않습니다.&quot;);
        }
    }


    //복사 명령문
    private static void copy_cmd(String[] arg) throws Exception {
        if (arg.length != 3) {
            System.out.println(&quot;사용법 : copy 원본 파일 복사 파일명&quot;);
            return;
        }
        System.out.println(path+&quot;/&quot;+arg[1]);
        File file = new File(path+&quot;/&quot;+arg[1]);
        File new_file = new File(path+&quot;/&quot;+arg[2]);

        if (validFile(file,arg[1])) {
            copy(file, new_file);
        }else if(validFolder(file,arg[1])) {
            copyDirectory(file, new_file);
        }
        else {
            System.out.println(arg[1] + &quot;이름의 해당 파일은 존재하지 않습니다.&quot;);
        }
    }

    // 파일 읽기 명령문
        private static void type_cmd(String[] arg) throws Exception {
            if (arg.length != 2) {
                System.out.println(&quot;사용법 : type 찾고자 하는 파일명&quot;);
                return;
            }
            File file = new File(path+&quot;/&quot;+arg[1]);
            if (validRead(file)) {
                BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file), &quot;UTF-8&quot;));
                while (true) {
                    String str = br.readLine();
                    if (str == null)
                        break;
                    System.out.println(str);
                }
                br.close();
            } else {
                System.out.println(arg[1] + &quot;이름의 해당 파일은 존재하지 않습니다.&quot;);
            }
        }

    // 순수 파일 복사 
        private static void copy(File dir, File new_dir) throws IOException{
            try (FileInputStream fis = new FileInputStream(dir);
                     FileOutputStream fos = new FileOutputStream(new_dir)) {
                    byte[] buffer = new byte[4096];
                    int bytesRead;
                    while ((bytesRead = fis.read(buffer)) != -1) {
                        fos.write(buffer, 0, bytesRead);
                    }
                    fos.flush();
                }
        }

        // 파일이 아닌 디렉토리일 경우 넘어가는 코드
        private static void copyDirectory(File dir, File new_dir) throws IOException {
            File[] children = dir.listFiles();

            if (children != null) {

                for (File child : children) {
                    File destChild = new File(new_dir, child.getName());

                    if (child.isDirectory()) {
                        destChild.mkdirs();
                        copyDirectory(child, destChild);
                    } else {
                        copy(child, destChild);
                    }

                }
            }


        }


    public static void main(String[] args) throws Exception {
        while (true) {
            System.out.print(path + &quot;&gt;&quot;);
            String str = sc.nextLine();
            if (&quot;exit&quot;.equals(str)) {
                // 프로그램 종료
                System.exit(0);
            }
            String[] arg = str.split(&quot; &quot;);
            switch (arg[0]) {
            case &quot;cd&quot;: {
                // 이동하기
                cd_cmd(arg);
                break;
            }
            case &quot;dir&quot;: {
                // 파일 내용 출력
                dir_cmd();
                break;
            }
            case &quot;*&quot;: {
                // 파일 전체 내용 출력
                dirAll_cmd();
                break;
            }
            case &quot;rename&quot;: {
                // 파일 이름 다시 만들기
                rename_cmd(arg);
                break;
            }
            case &quot;copy&quot;: {
                // 파일 카피
                copy_cmd(arg);
                break;
            }
            case &quot;type&quot;: {
                // 타입 파일오픈해서 열기
                type_cmd(arg);
                break;
            }
            default:
                throw new IllegalArgumentException(&quot;Unexpected value: &quot; + arg[0]);
            }

        }
    }
}
</code></pre>
<p>프로젝트로 만든 cmd를 자바 코드로 구현하는 토이 프로젝트다.</p>
<h2 id="링크">링크</h2>
<p><a href="https://github.com/Kyj-8754/msa2025-hello/tree/main/src/project/cmd">깃 허브</a></p>
<h2 id="뭘-만들었는가">뭘 만들었는가?</h2>
<p>최대한 cmd에서 기능하는 기능을 구현하려고 만든것들 이며
만든것은 </p>
<ul>
<li>현재 디렉토리 파일 확인<ul>
<li>현재 디렉토리+하위 디렉토리 파일확인</li>
<li>파일 카피</li>
<li>폴더 카피</li>
<li>메모장 열어서 콘솔에 노출하기</li>
<li>디렉토리 이동</li>
</ul>
</li>
</ul>
<p>이렇게 6가지를 구현하였다.</p>
<h2 id="만들면서-직면한-문제">만들면서 직면한 문제?</h2>
<p>구현하면서 가장 까다로웠던 것은 <code>파일 경로확인, 재귀함수 구현, 파일 복사 저장타입</code> 문제 였던것 같다.</p>
<p>파일 경로는 각 기능에서 올바른 경로를 받아와야 inputstream이나 File 라이브러리가 제대로 작동 하기 때문에 systeam.out을 통해서 매 동작마다 올바르게 받아 오는지 확인 하는 작업을 수행했다.</p>
<p>덕분에 path부분을 난잡하게 만들어서 매우 아쉽다. 이부분을 좀 더 다듬는다면 좀 더 직관적인 코드가 만들어 지지 않을까? 하는 생각이다.</p>
<p>파일 복사의 경우 UTF-8저장만 고려하면 되는게 아닐까? 하는 생각으로 아무 생각없이 BufferedReader를 사용해서 만들었다. OutputStream만 하면 끝나겠지 하고 만들었더니, ANSI는 둘째치고 windows로 저장되었던게 복사를 하니 unix로 만들어지는 불상사가 생겼다.</p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/91f8f5b4-2ecb-423b-afcb-0eaddfdfdc73/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/f834cae2-fd96-4fea-89d1-412f64b7b2e4/image.png" alt=""></p>
<blockquote>
<p>분명 windows인데...  저장하니까 Unix라고!? </p>
</blockquote>
<p>보통 이러면 무슨 문제가 생기는지 한번도 고려한적이 없었다
<img src="https://velog.velcdn.com/images/kyj-8754/post/96a0fd97-1416-4f69-8f58-baf6e7316aa3/image.png" alt="">
<img src="https://velog.velcdn.com/images/kyj-8754/post/428a84ac-e50b-42b2-bea2-c62bcf6efb28/image.png" alt=""></p>
<p>그러나 저장된 파일을 열어보니 위와 같이 불필요한 기호가 생겼다.</p>
<p>무지몽매한 나의 지식으로는 한계가 있어 gpt의 도움을 받으니 다음과 같은 말을 알려줬다</p>
<blockquote>
<p>BufferedReader의 readLine() 메서드는 한 줄씩 읽을 때 줄바꿈 문자를 제거합니다.
즉, 원본 파일이 Windows 스타일의 줄바꿈(CRLF, &quot;\r\n&quot;)을 가지고 있더라도, readLine()은 이를 제거하고 순수 텍스트 라인만 반환합니다.
그 후에 OutputStream(또는 OutputStreamWriter)으로 &quot;\n&quot;을 추가하면, 이는 Unix 스타일의 줄바꿈(LF)으로 처리되므로 결과 파일이 Unix 스타일로 만들어지는 것입니다.</p>
</blockquote>
<p>그렇다, readLine()메서드를 사용해서 값을 불러오고 그걸 하나씩 읽어 저장하는 방식을 사용했더니 줄바꿈이나 기타 명령문을 전부 지우고 저장하는, unix방식으로 저장해버리는 것이였다.</p>
<p>게다가 이러한 방식으로는 메모장같은건 만들지 몰라도 이미지는 복사를 못하는걸 알게 되었다.</p>
<p>그럼 다른 방식을 하면되는게 아닌가? byte단위로 쪼개서 저장하고 그걸 읽어오면 되는게 아닌가? 라는 생각을 했고</p>
<pre><code class="language-java">private static void copy(File dir, File new_dir) throws IOException{
            try (FileInputStream fis = new FileInputStream(dir);
                     FileOutputStream fos = new FileOutputStream(new_dir)) {
                    byte[] buffer = new byte[4096];
                    int bytesRead;
                    while ((bytesRead = fis.read(buffer)) != -1) {
                        fos.write(buffer, 0, bytesRead);
                    }
                    fos.flush();
                }
        }</code></pre>
<p>위와 같이 바이트를 담을 배열을 만들어서 이를 이용해서 버퍼에 담아 read하도록 만들었더니 성공적으로 담아왔다.</p>
<p>이미지 역시 깨지지 않고 잘 가져왔다 매우 성공적이였다.</p>
<p>마지막으로 폴더를 복사하는것에 대해서 문제가 생겼었다.
파일이야 어차피 1개를 복사하는 간단(?)한 문제였으나 폴더를 복사하기 위해서는 폴더 안에 파일리스트들의 경로를 읽어서 다시 카피명령문으로 보내야했기 때문에 재귀함수가 필요했다.</p>
<p>거기서 폴더명은 바뀌는 폴더명으로 바뀐다 할지라도, 안의 파일명은 바뀌면 안돼는 조건이 있다는걸 인지하고 만들어야 했다.</p>
<pre><code class="language-java">
        if (validFile(file,arg[1])) {
            copy(file, new_file);
        }else if(validFolder(file,arg[1])) {
            copyDirectory(file, new_file);
        }</code></pre>
<p>일단은 이것이 파일인지, 폴더인지 확인하는 if문을 만들었고 그에 따라 다음 메서드로 보내도록 만들어 구분을 지어줬다.</p>
<pre><code class="language-java">// 파일이 아닌 디렉토리일 경우 넘어가는 코드
        private static void copyDirectory(File dir, File new_dir) throws IOException {
            File[] children = dir.listFiles();

            if (children != null) {

                for (File child : children) {
                    File destChild = new File(new_dir, child.getName());

                    if (child.isDirectory()) {
                        destChild.mkdirs();
                        copyDirectory(child, destChild);
                    } else {
                        copy(child, destChild);
                    }

                }
            }


        }</code></pre>
<p>다음 디렉토리를 받아 파일 리스트를 만들고, 리스트를  for문으로 돌릴 때 그것이 폴더일 경우 mkdirs()메서드로 만들고, 다시 이 경로들을 업데이트해서 보냈고, 파일일 경우 카피하도록 copy메서드로 보냈다. </p>
<p>이렇게 함으로서 리스트가 제대로 반영 되게 만들었다.</p>
<h2 id="아쉬운점">아쉬운점</h2>
<p>토이 프로젝트로서 정해진 시간에 구현해야하다보니 파일 복사명령문 중복처리를 고려를 못했다. 원래는 <code>&#39;파일명 - 복사본&#39;</code> 이렇게 저장되도록 유도하는 명령문을 처리했어야 했는데 그게 아쉽다...</p>
<p>두번째로는 dir 명령문 보낼때와 dir /s 명령문 처리다. 원래대로라면 arg[1]에 값이 /s인지, 비어있는지 확인하고 전체를 불러올지, 혹은 현재 디렉토리만 서칭할지 만들어야했다 사실 머리속으로는 인지했는데 이것도 나의 나태함이 코드를 못짠게 아닐까.. 한다</p>
<p>세번째로는 path처리다, 현재 코드를 보면 알다시피 어떤건 /를 붙이고 어떤건 덜 붙이고 난잡하다. 이를 통일한다면 하나의 메서드로 처리할 수 있을것 같은데 이부분도 고려를 더 해봐야 할 것 같다.</p>
<p>네번째로는 다형성 부여다 boolean으로 처리하는 명령문이 사실 조건만 다르고 나머진 같은 형식이라서 추상메서드를 구현해보고 싶었다. swith-case문 역시 가능하다고 들었는데 나의 지식이 아직은 모자른것 같다.</p>
<p>다섯번째로는 gpt의존인것같다. 코드공부를 허투루한건지 머릿속에 알고리즘이 딱하고 나오질 못하고있다. 나도 안보고 샤샤샥 하고 코드를 짜고싶은데 한참 모자르다 ㅜㅜ</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[이것이 자바다 5장 확인문제풀이]]></title>
            <link>https://velog.io/@kyj-8754/%EC%9D%B4%EA%B2%83%EC%9D%B4-%EC%9E%90%EB%B0%94%EB%8B%A4-5%EC%9E%A5-%ED%99%95%EC%9D%B8%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4</link>
            <guid>https://velog.io/@kyj-8754/%EC%9D%B4%EA%B2%83%EC%9D%B4-%EC%9E%90%EB%B0%94%EB%8B%A4-5%EC%9E%A5-%ED%99%95%EC%9D%B8%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4</guid>
            <pubDate>Tue, 04 Mar 2025 10:33:13 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-java">package ch05.sec06;

import java.util.Scanner;

public class Exam {

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        System.out.println(&quot;------------------------------------------------------------------------------------&quot;);
        System.out.println(&quot;예제 7번&quot;);
        // 예제 7번
        // 주어진 배열에서 최대 값을 도출하는 코드
        int[] array = { 1, 5, 3, 8, 2 };
        int answer = 0; // 결과값 저장할 곳

        for (int i = 0; i &lt; array.length; ++i) {
            boolean bettwen = (answer &lt; array[i]); // anwser 이 더작으면 true를 반환

            if (bettwen) {
                answer = array[i]; // 작을 경우 anwser에 array 값을 집어넣음
            }
        }
        System.out.println(answer); // 반복문 나온 뒤 답 도출
        System.out.println(&quot;------------------------------------------------------------------------------------&quot;);
        System.out.println(&quot;예제 8번&quot;);
        // 예제 8번 주어진 배열의 합과 평균 구하는 코드
        /*
         * 해결 방안
         */

        // 주어진 예시
        int[][] arrary = { { 95, 86 }, { 83, 92, 96 }, { 78, 83, 93, 87, 88 } };

        int count = 0; // 값이 존재하는 길이 카운트
        int total_score = 0; // 총합
        double avg_score = 0.0; // 평균

        // 반복문
        for (int i = 0; i &lt; arrary.length; i++) {
            // 전체 합
            for (int j = 0; j &lt; arrary[i].length; j++) {
                total_score += arrary[i][j];
            }
            count += arrary[i].length;
        }

        // double 캐스팅
        avg_score = (double) total_score / count;

        System.out.println(&quot;전체 합 : &quot; + total_score);
        System.out.println(&quot;평균     : &quot; + avg_score);
        System.out.println(&quot;------------------------------------------------------------------------------------&quot;);
        System.out.println(&quot;예제 9번&quot;);
        // 예제 9번
        // 주어진 배열 항목에 전체 합과 평균을 구해 출력하는 코드

        /*
         * 학생 점수를 입력받아 값을 저장하고 분석하는 프로그램 while문과 Scanner의 nextLine() 함수를 이용하자 최고 점수 및 평균
         * 점수를 출력하자
         */

        // 문자열로 입력 될 경우 에러가 뜨기때문에 추후 개선 필요
        Scanner sc = new Scanner(System.in); // 스캐너 객체 생성

        int sel = 0; // 선택
        int student_count = 0; // 학생 수
        int[] scores = null; // 학생 점수 저장 배열
        int topScore = 0; // 학생 최고점
        int total = 0; // 전체 점수
        Double avg = 0.0; // 평균 점수

        // 프로그램 시작
        program: while (true) {
            System.out.println(&quot;-----------------------------------------------------------&quot;);
            System.out.println(&quot;1. 학생수 | 2. 점수입력 | 3. 점수리스트 | 4. 분석 | 5. 종료&quot;);
            System.out.println(&quot;-----------------------------------------------------------&quot;);
            System.out.print(&quot;선택&gt; &quot;);
            sel = Integer.parseInt(sc.nextLine()); // 원하는 버튼 선택

            switch (sel) {
            case 1: {
                System.out.print(&quot;학생수&gt; &quot;);
                student_count = Integer.parseInt(sc.nextLine());
                scores = new int[student_count]; // 학생 몇명 있는지 값을 받아 배열 크기 설정
                break;
            }
            case 2: {
                // 학생 점수 입력 반복문
                for (int i = 0; i &lt; student_count; i++) {
                    System.out.print(&quot;scores[&quot; + i + &quot;]&gt; &quot;);
                    scores[i] = Integer.parseInt(sc.nextLine());
                }
                break;
            }
            case 3: {
                // 점수 출력 반복문
                for (int i = 0; i &lt; scores.length; i++) {
                    System.out.println(&quot;scores[&quot; + i + &quot;]&gt; &quot; + scores[i]);
                }
                break;
            }
            case 4: {
                // 토탈 점수
                for (var score : scores) {
                    total += score;
                    if (topScore &lt; score) {
                        topScore = score;
                    }
                }
                // 평균 점수 double casting
                avg = (double) total / student_count;

                System.out.println(&quot;최고 점수: &quot; + topScore);
                System.out.printf(&quot;평균 점수: %.1f%n&quot;, avg);
                break;
            }
            case 5: {
                // 프로그램 올 스탑, 스캐너 메서드 종료
                System.out.println(&quot;프로그램 종료&quot;);
                sc.close();
                break program;
            }
            default: {
                // 선택지 제외 다른 숫자 오류 표기
                System.out.println(&quot;선택지에 맞게 다시 선택해 주세요&quot;);
            }
            }

        }

    }
}</code></pre>
<p>코드 리뷰를 주석에 달아만든 코드, 차후 개선 사항을 밑에 추가해야한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[25-03-04 SOLID 개발원칙]]></title>
            <link>https://velog.io/@kyj-8754/25-03-04-SOLID-%EA%B0%9C%EB%B0%9C%EC%9B%90%EC%B9%99</link>
            <guid>https://velog.io/@kyj-8754/25-03-04-SOLID-%EA%B0%9C%EB%B0%9C%EC%9B%90%EC%B9%99</guid>
            <pubDate>Tue, 04 Mar 2025 10:08:36 GMT</pubDate>
            <description><![CDATA[<h1 id="solid-원칙">SOLID 원칙</h1>
<p>S : 단일 책임 원칙(SRP_Single Responsibility Principle)
O : 개방 폐쇄 원칙(OCP_Open Closed Principle)
L : 리스코프 치환 원칙(LSP_Liskov Substitution Principle)
I : 인터페이스 분리 법칙(ISP_Interface Segregation Principle)
D : 의존 역전 원칙(Dependency Inversion Principle)</p>
<blockquote>
<p>SOLID원칙이란 객체지향(OOP) 설계에서 지켜야 할 5개의 소프트웨어 개발원칙을 의미한다.</p>
</blockquote>
<p>쉽게말하면 좋은 설계(클린 코드)를 통해 확장성이 용이하고 유지보수관리가 쉬우며, 복잡성을 제거해 프로젝트
개발의 생산성을 높이기 위해 만든 원칙</p>
<h2 id="s--단일-책임-원칙srp_single-responsibility-principle">S : 단일 책임 원칙(SRP_Single Responsibility Principle)</h2>
<p>단일 책임 원칙이란 클래스(객체)는 단 하나의 책임만 가져야 한다를 의미한다.
    하나의 클래스에는 하나의 기능만 내포하라는 의미, 기능이 여러개일 경우 유지 보수 관리에 불리하다.
    단, 하나의 기능이란 단어에 메달려 하나의 메서드만을 내포한다고 생각해서는 안된다. 하나의 책임이란
    어떤 책임을 가지는 자가 그 책임을 맡기 위해 하는 여러 행위(메서드)를 포함하고있다.</p>
<pre><code>EX) 경비원이 순찰, 단속, 안내를 맡을 수 있지만 화장실 청소를 하지는 않는다.</code></pre><h2 id="o--개방-폐쇄-원칙ocp_open-closed-principle">O : 개방 폐쇄 원칙(OCP_Open Closed Principle)</h2>
<p>확장에 열려있으며, 수정에는 닫혀있어야 한다를 의미
    기능 추가, 변경에 있어 추가에는 쉽게, 변경에는 최소화로 하라는 의미이다.
    추상화를 통한 다형성확보와 확장성극대화를 추구한다.</p>
<blockquote>
<p>추상화란? : 핵심적인 부분을 남기고 불필요한 부분을 제거하는 행위를 의미한다.</p>
</blockquote>
<h2 id="l--리스코프-치환-원칙lsp_liskov-substitution-principle">L : 리스코프 치환 원칙(LSP_Liskov Substitution Principle)</h2>
<p>상속관계인 서브(자식)타입은 언제나 기반(부모)타입으로 교체될 수 있어야 한다는 의미
    다향성 원리를 이용하기 위한 원칙이다
    부모의 기능을 자식이 마음대로 바꿔선 안되며, 상속을 받더라도 부모클래스의 기능은 정상적이여야 한다는 의미
    단, 상속 관계가 무조건 정답이 아니기에 확장 대신 합성 또한 고려해야 한다.</p>
<h2 id="i--인터페이스-분리-법칙isp_interface-segregation-principle">I : 인터페이스 분리 법칙(ISP_Interface Segregation Principle)</h2>
<p>SOP와 비슷하지만 다른 원리, 인터페이스를 용도에 맞게 분리해야함을 의미
    클라이언트의 목적과 용도에 맞게 적합한 인터페이스를 제공 할 수 있도록 해야한다.
    단 한번 분리한 인터페이스는 차후 다시 분리하는 행위가 발생하지 않도록 해야한다.
    즉 클래스에서 불필요한 메서드를 실행할 필요없도록 인터페이스를 분리하는 행위가 인터페이스 분리 법칙이다.</p>
<h2 id="d--의존-역전-원칙dependency-inversion-principle">D : 의존 역전 원칙(Dependency Inversion Principle)</h2>
<p>어떤 클래스를 참조해야하는 상황이 올 경우, 그 클래스의 상위 요소(추상 클래스 OR 인터페이스)를 참조하라는 의미
    클래스 간의 의존성이 과도하게 연결 되어 있을 경우, 클래스의 코드를 바꿔야 할 때 전부 바꿔야 할 수 있기에 상위 클래스 혹은 인터페이스를 만들어 코드를 간결하고 수정하기 편하게 만드는것에 있어서 개방 폐쇄 원칙과 밀접한 관계가 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[25-2-27 자바 디버깅]]></title>
            <link>https://velog.io/@kyj-8754/25-2-27-%EC%9E%90%EB%B0%94-%EB%94%94%EB%B2%84%EA%B9%85</link>
            <guid>https://velog.io/@kyj-8754/25-2-27-%EC%9E%90%EB%B0%94-%EB%94%94%EB%B2%84%EA%B9%85</guid>
            <pubDate>Thu, 27 Feb 2025 06:54:26 GMT</pubDate>
            <description><![CDATA[<p>디버깅 관하여 잠깐 글 끄적..</p>
<pre><code>package ch05.sec05;

public class EqualsExample {
    public static void main(String[] args) {
        String strVar1 = &quot;홍길동&quot;;
        String strVar2 = &quot;홍길동&quot;;

        if(strVar1 == strVar2) {
            System.out.println(&quot;strVar1과 strVar2는 참조가 같음&quot;);
        } else {
            System.out.println(&quot;strVar1과 strVar2는 참조가 다름&quot;);
        }

        if(strVar1.equals(strVar2)) {
            System.out.println(&quot;strVar1과 strVar2는 문자열이 같음&quot;);
        }

        String strVar3 = new String(&quot;홍길동&quot;);
        String strVar4 = new String(&quot;홍길동&quot;);

        if(strVar3 == strVar4) {
            System.out.println(&quot;strVar3과 strVar4는 참조가 같음&quot;);
        } else {
            System.out.println(&quot;strVar3과 strVar4는 참조가 다름&quot;);
        }

        if(strVar3.equals(strVar4)) {
            System.out.println(&quot;strVar3과 strVar4는 문자열이 같음&quot;);
        }
    }
}</code></pre><p>예시 코딩인데 String의 참조 변수(strVar1, strVar2)의 스택(stack)을 확인하려고 한다.</p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/d47d321e-58be-404d-9a36-1c7976e14834/image.png" alt=""></p>
<p>원하는 코딩줄에 우클릭을 하게되면 Toggle Breakpoint를 잡아 줄  수 있다.</p>
<p>F11키 혹은 run에 Debug를 누르게 되면해당 줄까지 run을 하게 된다. </p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/7d0df380-0604-4939-8b34-08a7a25fe1a4/image.png" alt=""></p>
<p><code>run한 모습, 8번줄에 하이라이트가 들어가 있다.</code></p>
<p>우측을 확인해보면 참조 변수(strVar1, strVar2)의 id가 확인 가능하다, String을 새롭게 지정하지 않았기에 id값(Stack 주소)이 22로 같다. 이런다음 F5(Run-Step into), F6(Run - Stepver)을 누르면 다음 줄을 진행하는 방식이다.</p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/262d2ff8-7a1f-4217-9d11-b9c96fcd3cbd/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/39e747dd-1884-4d09-b062-1f393d37faf7/image.png" alt="">
<code>id값이 22로 같기 때문에 참조는 같다고 나오는중</code></p>
<p>F6을 두번 눌러 9번까지 진행하고 하여 콘솔창에 줄이 출력된다.</p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/da7e3e70-61f2-4d59-95d2-c89c790a8aee/image.png" alt="">
<code>String 클래스에 equals() 메서드가 현재 어디까지 진행 되어있는지 보여주고있다.</code></p>
<p>F5를 누르게 되면 클래스에 메서드까지 들어가서 현재 어떤 코드가 진행되는지까지 보여주기도 한다.</p>
<p>여기서 메서드 진행을 보지않고 넘어가려면 F7(Run - Step Return)을 써서 빠져나오면 되고, 더이상 진행을 보지 않으려면 중지를 하거나, F8(Run - Resume)을 해서 나머지 코드를 전부 Run해주자.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[R & RStudio]]></title>
            <link>https://velog.io/@kyj-8754/R-RStudio</link>
            <guid>https://velog.io/@kyj-8754/R-RStudio</guid>
            <pubDate>Mon, 20 Nov 2023 03:40:05 GMT</pubDate>
            <description><![CDATA[<h1 id="1-r이란">1. R이란?</h1>
<ul>
<li>R은 통계 및 데이터 분석을 위한 프로그래밍 언어 및 환경으로, 특히 통계학, 데이터 마이닝, 머신 러닝 등과 관련된 작업에 많이 사용된다.</li>
<li>데이터 사이언스 분야에서 자주 쓰이고 데이터 분석에 특화된 언어이다. 또한 오픈소스 이기 때문에 쉽게 배울 수 있다.</li>
<li>오픈소스 </li>
<li>데이터 시각화</li>
<li>통계 분석 및 모델링</li>
<li>데이터 조작 및 전처리</li>
</ul>
<h1 id="2-r-설치">2. R 설치</h1>
<p><a href="https://www.r-project.org/about.html">https://www.r-project.org/about.html</a></p>
<ul>
<li>Cran</li>
</ul>
<p><a href="https://cran.yu.ac.kr/">https://cran.yu.ac.kr/</a>
 Download</p>
<h1 id="3-rstudio-설치">3. RStudio 설치</h1>
<p><a href="https://posit.co/download/rstudio-desktop/">https://posit.co/download/rstudio-desktop/</a></p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/935f7638-e0e5-4fad-8f81-7d93d3ef45ae/image.png" alt=""></p>
<h1 id="4-숫자">4. 숫자</h1>
<ul>
<li>정수(Integer), 실수(Double)</li>
<li>매우 큰 수와 작은 수는 &#39;E&#39; 또는 &#39;e&#39;를 활용하여 표기</li>
<li>Inf는 무한대, NaN(Not a Number, 숫자가 아님)를 뜻함</li>
</ul>
<h1 id="5-문자">5. 문자</h1>
<ul>
<li>작은 따옴표 또는 큰 따옴표를 문자의 시작과 끝을 나타냄</li>
</ul>
<h1 id="6-논리값">6. 논리값</h1>
<ul>
<li>참/거짓을 표현하기 위해 TRUE/FALSE를 사용</li>
<li>대소문자 명확히 구분</li>
<li>값이 비어있는 결측값 표기시에는 NA 또는 NULL로 사용</li>
</ul>
<h1 id="7-연산자">7. 연산자</h1>
<h2 id="7-1-할당연산자">7-1. 할당연산자</h2>
<ul>
<li><p>특정 객체(Object)에 값을 할당하기 위한 연산자</p>
</li>
<li><p>&quot;=&quot;와 &quot;&lt;-&quot;는 용법의 차이가 거의 없음</p>
</li>
<li><p>화살표 모양의 연산자를 사용하는 경우 화살표의 머리에 따라 할당하는 방향이 다름</p>
</li>
<li><p>&#39;&lt;&lt;-&#39; 와 &#39;-&gt;&gt;&#39; 는 전역변수 할당시 사용 가능</p>
<p> x = 1     # x에 1을 대입
 x &lt;- 1     # x에 1을 대입
 1 -&gt; x     # x에 1을 대입
 x &lt;&lt;- 1     # x에 1을 대입
 1 -&gt;&gt; x    # x에 1을 대입</p>
</li>
</ul>
<h2 id="7-2-산술-연산자">7-2. 산술 연산자</h2>
<ul>
<li><p>두 숫자를 연산하기 위해 사용하는 연산자</p>
<ul>
<li>덧셈</li>
<li>뺄셈, 음수 부호</li>
<li>곱셈
/    나눗셈
%/%    몫
%%    나누기
^or**    지수 연산</li>
</ul>
</li>
</ul>
<h2 id="7-3-비교-연산자">7-3. 비교 연산자</h2>
<ul>
<li><p>두 자룔르 서로 비교하는 참 또는 거짓 결과를 반환</p>
</li>
<li><p>결과는 TRUE, FALSE</p>
</li>
<li><p>비교 연산자는 기본적으로 1:1 또는 1:n의 비교가 원칙이나,
&#39;%in%&#39; 연산자의 경우는 n:n의 비교 가능</p>
<p> ==    같다
 !=    같지 않다
 &lt;    미만</p>
<blockquote>
<p>   초과
 &lt;=    이상
=    이하
 &amp;    논리(AND)
 |    논리(OR)
 !    논리 부정
 %in%    일치 여부 검사(match)</p>
</blockquote>
</li>
</ul>
<h1 id="8-함수">8. 함수</h1>
<h2 id="8-1-기본-함수">8-1. 기본 함수</h2>
<ul>
<li>R을 구동한 이후 별도의 조치 없이 사용</li>
<li>base, statuts, Utils등 기본 패키지의 함수</li>
</ul>
<h2 id="8-2-패키지-함수">8-2. 패키지 함수</h2>
<ul>
<li>특정 패키지를 설치 후 불러와야 사용할 수 있는 함수</li>
</ul>
<h2 id="8-3-사용자-정의-함수">8-3. 사용자 정의 함수</h2>
<ul>
<li>기본 함수, 패키지 함수 등 다양한 요소를 조합해서 사용자가 직접 만든 함수</li>
</ul>
<h1 id="9-도움말">9. 도움말</h1>
<ul>
<li>help() : 특정 함수의 설명을 조회하기 위해 사용하는 함수</li>
<li>help.search() : 특정 단어가 들어간 도움말을 조회할 때 사용하는 함수</li>
<li>? : help() 함수 대응</li>
<li>?? : help.search() 함수 대응</li>
</ul>
<h1 id="10-패키지">10. 패키지</h1>
<ul>
<li>R의 사용자 정의함수 또는 별도의 데이터를 묶어놓은 것으로 라이브러리(lib)
 라고도 한다.</li>
<li>기본 패키지, 공식 패키지, 비공식 패키지</li>
<li>기본 패키지 : R 설치시 기본 제공되는 패키지이며 일부는 R 구동시 자동으로 불러옴</li>
<li>공식 패키지 : 각종 OS에서 동작을 보장하며 RCran 사이트에서 다운로드 가능하다</li>
<li>비공식 패키지 : 비공식 패키지는 RCran에서 다운로드 불가능하고, 별도의 웹페이지
접속 또는 패키지 사용 필요</li>
<li>패키지의 함수 또는 데이터를 사용하기 위해서는 libaray() 함수를 사용해서 불러오는
것이 일반적</li>
</ul>
<h2 id="10-1-패키지-설치하기">10-1. 패키지 설치하기</h2>
<ul>
<li>install.packages() 함수를 사용하여 공식 패키지 다운로드</li>
<li>압축파일을 바로 설치하는 경우 install.packges() 함수에서 type=&#39;source&#39;설정 필요</li>
<li>비공식 패키지 다운로드시 devtools 또는 remotes패키지 주로 사용</li>
<li>패키지 제거는 remove.packges() 함수로 가능</li>
</ul>
<h2 id="10-2-패키지-불러오기">10-2. 패키지 불러오기</h2>
<ul>
<li>library() 함수를 사용하여 불러오며 패키지명은 따옴표로 감싸서 명시할 수 있다.</li>
<li>패키지가 없다는 에러가 발생할 경우 패키지명을 제대로 입력하지 않았거나, 패키지가 설치되지않은 경우</li>
<li>&quot;::&quot; 기호를 패키지명 뒤에 적게 되면 해당 패키지의 함수 또는 데이터 목록 확인이 수월하며 library() 함수 없이 사용이 가능하다.</li>
</ul>
<h1 id="11-객체">11. 객체</h1>
<ul>
<li>보통 데이터를 담아두는 용기에 비유</li>
<li>데이터 속성과 구조에 따라 사용할 수 있는 함수가 다름</li>
<li>대표적으로 1차원 벡터, 메트릭스 , 데이터 프레임, 리스트</li>
<li>비어있는 객체 생성 가능</li>
</ul>
<h2 id="11-1-관련함수">11-1. 관련함수</h2>
<ul>
<li>ls() : 등록된 객체 목록을 출력</li>
<li>object.size() : 특정 객체의 용량 확인 가능</li>
<li>format() : object.size()함수의 출력물을 utlils 인자를 활용하여 KB, MB등 각 자료 단위로 표기 가능</li>
<li>rm() :  등록된 객체를 제거하고자 할때 사용</li>
</ul>
<h1 id="12-벡터">12. 벡터</h1>
<h2 id="12-1-1차원-벡터">12-1. 1차원 벡터</h2>
<ul>
<li>숫자/문자/논리값 등을 원소로 하는 1차원 객체<ul>
<li>지정한 순서대로 값이 위치하며 두 개 이상의 서로 다른 속성이 할당되는 경우 하나로 통일</li>
</ul>
</li>
<li>문자는 숫자보다 우선시 되며, 문자와 숫자를 할당할 경우 숫자도 문자로 자동 변환<ul>
<li>c() 함수로 생성하며 이는 각 원소를 결합</li>
<li>1차원 벡터는 각 원소에 이름 지정 가능하다.</li>
<li>&#39;letters&#39;와 &#39;LETTERS&#39;는 사전에 등록되어 있으나 내부 원소 치환 가능</li>
</ul>
</li>
</ul>
<h1 id="13">13.</h1>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Boot 12일차]]></title>
            <link>https://velog.io/@kyj-8754/Spring-Boot-12%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@kyj-8754/Spring-Boot-12%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Thu, 12 Oct 2023 07:16:43 GMT</pubDate>
            <description><![CDATA[<h1 id="1-jpql-문법">1. JPQL 문법</h1>
<blockquote>
<p>&quot;select m from Member as m where m.age &gt; 18&quot; 이라는 DB문을 쓸때 자바에서는 무엇을 주의 해야할까?</p>
</blockquote>
<ul>
<li>Member : 대소문자 구분을 주의하자. java클래스는 대소문자를 구분하기 때문, 테이블 이름이 아닌, 엔티티 이름을 사용한다.</li>
<li>엔티티 이름을 @Entity(name = &quot;mm&quot;) 으로 정해줄 경우, jpql에서도 mm으로 사용한다는 뜻
그러나 이런방식은 흔히 사용하지않으니 알아만 두자.
   -&gt; JPQL 키워드 : select, from, where 대소문자 구분안함.</li>
</ul>
<h2 id="1-1-집합과-정렬">1-1. 집합과 정렬</h2>
<pre><code>select 
    count(m),     // 회원 수
    sum(m.age),    // 나이 합
    avg(m.age),    // 평균 나이
    max(m.age),    // 최대 나이
    min(m.age)    // 최소 나이
from Member m

- group by, having
- order by</code></pre><blockquote>
<p>DB에서 SQL을 쓰던걸 잊지말자.</p>
</blockquote>
<h2 id="1-2-typequery-query">1-2. TypeQuery, Query</h2>
<ul>
<li>TypeQuer    : 반환 타입이 명확할 때 사용, 두번째 파라미터에 클래스를 잡아주는걸 볼 수 있다.</li>
</ul>
<pre><code class="language-java">    TypeQuery&lt;Member&gt; query =
    em.createQuery(&quot;select m from member m&quot;, Member.class);</code></pre>
<ul>
<li>Query    : 반환 타입이 명확하지 않을 때 사용<pre><code class="language-java">  Query query = 
 em.createQuery(&quot;select m.username, m,age from Member m&quot;);</code></pre>
</li>
</ul>
<h2 id="1-3-결과-조회">1-3. 결과 조회</h2>
<ul>
<li>query.getResultList()    : 결과가 하나 이상일 때, 리스트 반환
 -&gt; 결과가 없으면 빈 리스트 반환
 -&gt; 빈 collection이 반환되기 때문에 NullPointException에 대한 걱정은 안해도 된다.</li>
<li>query.getSingleList()    : 결과가 정확히 하나(조심), 단일 객체 반환
 -&gt; 결과가 없으면 : NoResultException
 -&gt; 결과가 둘 이상이면 : NonUniqueResultException 반환</li>
</ul>
<h2 id="1-4-파라미터-바인딩">1-4. 파라미터 바인딩</h2>
<p>파라미터는 방식에 따라 두가지로 선언하여 바인딩이 가능하다.</p>
<ul>
<li><p>이름 기준</p>
<pre><code> select m from Member m where m.username = :username

 query.setParameter(&quot;username&quot;, usernameParam);</code></pre></li>
</ul>
<ul>
<li><p>위치 기준</p>
<pre><code> select m from Member m where m.username = ?1

 query.setParameter(1, usernameParam);</code></pre></li>
</ul>
<h2 id="1-5-프로젝션">1-5. 프로젝션</h2>
<ul>
<li><p>select 절에 조회할 대상을 지정하는 것</p>
</li>
<li><p>프로젝션 대상 : 엔티티, 임베디드 타입, 스칼라 타입(숫자, 문자 등 기본 데이터 타입)</p>
<ul>
<li>select m from Member m </li>
<li><blockquote>
<p>Member 엔티티 조회</p>
</blockquote>
<ul>
<li>select m.team from Member m</li>
</ul>
</li>
<li><blockquote>
<p>Member와 관련된 team을 가지고 온다.</p>
</blockquote>
<ul>
<li>select m.address from Member m</li>
<li><blockquote>
<p>임베디드 타입을 불러온다.</p>
</blockquote>
</li>
<li>select m.username, m.age from Member m</li>
</ul>
</li>
<li><blockquote>
<p>스칼라 타입을 불러온다.</p>
</blockquote>
</li>
</ul>
</li>
</ul>
<h2 id="1-6-여러타입으로-조회">1-6. 여러타입으로 조회</h2>
<p>지금까지 지식을 바탕으로 조회하는것을 보자.</p>
<ul>
<li><p>프로젝션 타입.</p>
<pre><code class="language-java">List&lt;Member&gt; result
          = em.createQuery(&quot;select m from Member m&quot;, Member.class)
               .getResultList();

      Member findMember = result.get(0);
      findMember.setAge(20);
      /*--------------------------------------------------------*/

      /*
       * jpql입장에서는 select문이 나가지만,실제 sql입장에서는
       * member와 team을 조인해서 결과를 찾아야 한다.
       * 근데 join은 실제 sql문과 비슷하게 작성해야한다.
       */
      List&lt;Team&gt; result2 =
              em.createQuery(&quot;select m.team from Member m&quot;,Team.class)
                 .getResultList();

</code></pre>
</li>
</ul>
<pre><code>    List&lt;Team&gt; result3 =
            em.createQuery(&quot;select t from Member m join m.team t&quot;,Team.class)
               .getResultList();


    ///////////////////////////////////////////////////////////////
    //임베디드 타입
    em.createQuery(&quot;select o.address from Order o&quot;, Address.class)
      .getResultList();

    // Address 자체는 임베디드 타입일 뿐 엔티티가 아니다.따라서 다음과 같은 코드는 잘못되어있다.</code></pre><p>//        em.createQuery(&quot;select o.address from Address o &quot;, Address.class)
//        .getResultList();</p>
<pre><code>    ////////////////////////////////////////////////////////////////
    //스칼라 타입, 리턴타입이 없기 떄문에 두번째 파라미터는 없다.
    em.createQuery(&quot;select distinct m.username, m.age from Member m&quot;)
      .getResultList();</code></pre><pre><code>
### 1. 타입 미지정, 엔티티 조회

```java
        List resultList = 
                em.createQuery(&quot;select m.username, m.age from Member m&quot;)
                .getResultList();

        Object o = resultList.get(0);
        Object[] result = (Object[])o;
        System.out.println(&quot;username :&quot; + result[0]);
        System.out.println(&quot;age :&quot; + result[1]);</code></pre><p>타입을 지정하지 않았기 때문에 Object로 받아오는것이 보인다.  애플리케이션에서 실행을 한다면</p>
<pre><code>    username :member1
    age :10</code></pre><p>결과값을 보내준다. username과 age는 임의로 넣은 값으로 봐주면 될것 같다.</p>
<h3 id="2-타입지정-엔티티-조회">2. 타입지정, 엔티티 조회</h3>
<pre><code class="language-java">        List&lt;Object[]&gt; resultList2 = 
                em.createQuery(&quot;select m.username, m.age from Member m&quot;)
                .getResultList();

        Object[] result2 = resultList2.get(0);
        System.out.println(&quot;username :&quot; + result2[0]);
        System.out.println(&quot;age :&quot; + result2[1]);</code></pre>
<h3 id="3-객체-방식-조회">3. 객체 방식 조회</h3>
<pre><code class="language-java">    List&lt;MemberDTO&gt; result3 = 
                em.createQuery(&quot;select new com.codingbox.jpql.MemberDTO(m.username, m.age) from Member m&quot;)
                .getResultList();

        MemberDTO memberDTO = result3.get(0);
        System.out.println(&quot;memberDTO : &quot; + memberDTO.getUsername());
        System.out.println(&quot;memberDTO : &quot; + memberDTO.getAge());


        tx.commit();
        em.close();
        emf.close();</code></pre>
<p>MemberDTO 객체를 따로 만들어, 조회하는 방식이다. 가장 권장하는 방식이며, 객체지향방식에 어울리는 코딩이다.</p>
<h2 id="1-7-페이징-api">1-7. 페이징 API</h2>
<ul>
<li>JPA는 페이지를 다음 두 API로 추상화</li>
<li>setFirstResult(int startPoint): 조회 시작 위치(defalut 0부터 시작)</li>
<li>setMaxResults(int maxResult)    : 조회할 데이터 수
 -&gt; 몇 번째 부터 몇 건을 가지고 올래? 라고 묻는다.</li>
</ul>
<pre><code class="language-java"> List&lt;Member&gt; resultList = em.createQuery(jpql,Member.class)
                                    .setFirstResult(10)
                                    .setMaxResults(20)
                                    .getResultList();</code></pre>
<p>페이징처리를 한 구문만 가져왔다. 
<code>.setFirstResult(10)</code>를 선언 함으로써 10번째 부터 값을 가져오도록 설정을 해준다.
<code>.setMaxResults(20)</code>를 이용해서 20개의 값만 가져올거라 선언을 해준다.
이처럼 수많은 값들이 있을 경우, 페이징 처리를 통해 필요한 값만 가져올 수 있다. </p>
<h2 id="1-8-조인">1-8. 조인</h2>
<ul>
<li>문법이 객체 스타일로 나간다</li>
<li>내부조인
 -&gt; select m from Member m (inner 생략가능) join m.team t</li>
</ul>
<ul>
<li>외부 조인
 -&gt; selct t from Meber m left (outer 생략) join m.team t</li>
</ul>
<h2 id="1-9-서브쿼리">1-9. 서브쿼리</h2>
<ul>
<li>From절의 서브쿼리는 현재 JPQL에서 불가능하다, 조인으로 풀 수 있으면 풀어서 해결하자</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Boot 11일차]]></title>
            <link>https://velog.io/@kyj-8754/Spring-10%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@kyj-8754/Spring-10%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Wed, 11 Oct 2023 07:00:08 GMT</pubDate>
            <description><![CDATA[<h1 id="1-양방향-연관관계">1. 양방향 연관관계</h1>
<blockquote>
<p>Member 테이블과 Team 테이블이 있을 경우, 외래키를 Member에 지정하면서, Member가 Team을 가졌으나, Team은 Member의 정보를 가져오지 못하게 되어있다. 이를 단방향 연관 관계로 본다.</p>
</blockquote>
<pre><code class="language-java">    @ManyToOne
    @JoinColumn(name=&quot;TEAM_ID&quot;)
    @Setter(value=AccessLevel.NONE) // lombok에서 자동 setter를 막는다.
    private Team team;</code></pre>
<p>Member 클래스에 다음과 같이 일대다 연관 관계를 맺어 정보를 가져오지만, Team클래스에는 별도로 지정한것이 없기에 한 방향으로만 관계를 가지고 있다.</p>
<h1 id="2-객체와-테이블이-관계를-맺는-차이">2. 객체와 테이블이 관계를 맺는 차이</h1>
<h2 id="2-1-테이블-연관관계--1개">2-1. 테이블 연관관계 = 1개</h2>
<ul>
<li>회원 &lt;-&gt; 팀의 연관관계 1개(양방향)
 -&gt; MEMBER 테이블 입장에서 TEAM 테이블 조인 가능
 -&gt; TEAM 테이블 입장에서 MEMBER 테이블 조인 가능</li>
</ul>
<h2 id="2-2-객체-연관관계--2개">2-2. 객체 연관관계 = 2개</h2>
<ul>
<li>회원 -&gt; 팀 연관관계 1개 (단방향)</li>
<li>팀 -&gt; 회원 연관관계 1개 (단방향)
 -&gt; 사실은 단방향 연관관계가 2개 이어져있는 상태, 억지로 양방향이라고 말하는 것 실제와는 다르다.</li>
</ul>
<blockquote>
<p>DB에서는 외래키를 지정하게 해주면 쿼리문을 통해 조인이 양쪽으로 가능하다는걸 보여주지만
자바의 객체개념으로 보면 별도의 어노테이션을 이용해서 단방향을 서로 만들어 해주어야 한다는걸 보여주는 구문</p>
</blockquote>
<h1 id="3-둘-중-하나로-외래키를-관리해야-한다">3. 둘 중 하나로 외래키를 관리해야 한다.</h1>
<ul>
<li><p>Member에서 Team으로 가는 team 참조 값과, Team에서 Member로가는 members참조 값이 있다.</p>
</li>
<li><p>Member에서 Team값이 수정 됐을 때 MEMBER 테이블의 TEAM_ID가 수정이 되야 하는지, Team에 있는 members를 수정 했을 때 MEMBER에 있는 TEAM_ID가 수정이 되야 하는지?</p>
<p>=&gt; DB입장에서는 MEMBER에 있는 TEAM_ID만 update가 되면 된다.</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/7d05fb47-2a0e-4cc4-a325-2851fb902866/image.png" alt=""></p>
<p>즉 현재 보는것처럼 Member에 FK가 존재하기에, Member 클래스 에서 외래키를 관리한다.</p>
<h1 id="4-연관관계의-주인owner">4. 연관관계의 주인(Owner)</h1>
<ul>
<li>양방향 매핑 규칙<ul>
<li>객체의 두 관계중 하나를 연관관계의 주인으로 지정<ul>
<li>연관관계 주인만이 외래키를 관리(등록, 수정)할 수가 있다.</li>
</ul>
</li>
<li>주인이 아닌 쪽은 읽기만 가능</li>
<li>주인은 mappedBy 속성을 사용하지 않은 쪽이 주인이다.
-&gt; 내가 누군가에게 의해서 mapping이 되었다 라는 뜻</li>
<li>주인이 아니면 mappedBy 속성으로 주인을 지정</li>
<li>mappedBy가 적힌 곳은 읽기만 가능, 즉 값을 넣어도 update가 진행되지 않는다. 대신 조회는 가능</li>
</ul>
</li>
</ul>
<pre><code class="language-java">    @OneToMany(mappedBy = &quot;team&quot;)
    private List&lt;Member&gt; member = new ArrayList&lt;&gt;();</code></pre>
<blockquote>
<p>위 코드는 Team 클래스에 일대다 관계를 맺어주는 구문이다. 하나의 팀에 여러 멤버들이 있기 때문에, List를 선언하는 모습을 볼 수 있다.</p>
</blockquote>
<h1 id="5-주의사항">5. 주의사항</h1>
<pre><code class="language-java">        Member member = new Member();
        member.setUsername(&quot;member1&quot;);
        em.persist(member);

        Team team = new Team();
        team.setName(&quot;TeamA&quot;);
        team.getMember().add(member);
        em.persist(team);</code></pre>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/674685ad-946d-42d5-80b4-5d4ec2b13713/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/22537bd3-03a4-4a3f-8aff-b63df9330c7a/image.png" alt=""></p>
<ul>
<li>DB에서 조인을 통해 값을 불러왔더니 TEAM_ID에 null로 되어있다. MEMBER 테이블에 값이 들어가 있지 않은걸 확인했다.</li>
</ul>
<p>TEAM테이블이 먼저 지정이 되어야 TEAM_ID의 값이 들어가고, MEMBER 테이블에 TEAM_ID컬럼에 값을 넣어줄 수 있는데, 순서가 바뀌어있다. 그래서 코드에서는 <code>team.getMember().add(member);</code> 를 사용해서 멤버 클래스에 TEAM_ID에 값을 넣어주고 싶었으나, null값 즉, 값이 들어가지 않았음을 볼 수 있다.</p>
<blockquote>
<p>거듭 말하지만 현재 관계의 주인은 Member 클래스이다. 즉, Team클래스에서는 수정할 권한이 없단걸 인지하자.</p>
</blockquote>
<p>그렇다면 어떻게할까?</p>
<h3 id="member클래스owner-경우">Member클래스(Owner) 경우</h3>
<pre><code class="language-java">@Entity
@Getter @Setter
public class Member2 {

    @Id @Column(name=&quot;MEMBER_ID&quot;)
    @GeneratedValue
    private Long id;
    private String username;

    @ManyToOne
    @JoinColumn(name=&quot;TEAM_ID&quot;)
    @Setter(value=AccessLevel.NONE) // lombok에서 자동 setter를 막는다.
    private Team2 team2;


    public void setTeam(Team2 team2) {
        this.team2 = team2;
    }



}</code></pre>
<p>임시로만든 코드를 보자 Member2클래스에 Team2 클래스를 불러오도록 메서드 선언 해주고, </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();

        // 수정
        Team2 team2 = new Team2();
        team2.setName(&quot;TeamB&quot;);
        em.persist(team2);

        Member2 member2 = new Member2();
        member2.setUsername(&quot;member2&quot;);
        member2.setTeam(team2); // 연관관계 주인에게 값 설정
        em.persist(member2);



        tx.commit();
        em.close();
        emf.close();
    }</code></pre>
<p>위와 같이 member2에서 setTeam메서드를 불러와서 team2객체 값을 넣어주면 된다.
team2 객체에 추가한 값을 그냥 Member2에 set메서드를 통해 한번 선언 해주면 업데이트가 되는것이다.</p>
<h3 id="team클래스-owner-x-경우">Team클래스 (Owner X) 경우</h3>
<p>Team 객체에 값을 넣고, 생성된 값을 Member 클래스에 값을 업데이트하고 그 걸 다시 Team 클래스 즉, 자기 자기자신에게 다시 덮어 씌우는 2중 과정을 거쳐야한다.</p>
<pre><code class="language-java">    // Member 클래스에 추가한 메서드
    public void setTeam(Team team) {
        this.team = team;
    }

    // Team 클래스에 추가한 메서드, 
    public void addMember(Member member) {
        member.setTeam(this);
        this.member.add(member);
    }

    // 엔티티매니저에서 실행 구문
    team.addMember(member); // 1의 입장에서 값 넣기</code></pre>
<p>위 코드가 추가로 더 필요하다.</p>
<p>일대다인 시점의 Team 클래스에서는 member클래스를 불러와서 set하고 다시 그 인스턴스를 에 member 객체에 추가하는게 보인다. 서로 체인되는걸 제대로 인지 안한다면 어렵다..</p>
<ul>
<li>커밋 전 단계에서 member에 저장된 값을 select하려 했더니 Main 클래스에 insert문만 실행되고, select문은 실행되지 않았다.</li>
<li>team이 영속성 컨텍스트에 들어가 있는데, 현재는 member가 할당되지 않은 상태에서 team으로 검색을 하니 1차캐시에 담겨있는 내용 그대로가 조회가 된것</li>
<li>즉, 영속성 컨텍스트 영역사용시 team을 중심으로 member의 조회가 되지 않는다.
  해결 방법은 커밋 전, flush를 통해 DB로 날려주고 그 값을 다시 꺼내오는것, flush를 한 뒤에는 clear문을 선언해 주느것도 잊지말자.</li>
<li>순수 객체 상태를 고려해서 항상 양쪽에 값을 설정하자
 -&gt; 연관관계 편의 메서드를 생성하자</li>
<li>양방향 매핑시 무한 루프를 조심하자.
 -&gt; toString(), lombok 사용 시 조심 </li>
</ul>
<h1 id="6-연관관계-편의-메서드-생성">6. 연관관계 편의 메서드 생성</h1>
<p>사실 위처럼 EntityManager가 들어있는 클래스에 코드를 짜 넣을 필요없이, 각 객체에 메서드를 미리 짜두어 코드를 반복해서 사용할 필요없이 할 수 있다.</p>
<p>이렇게 한다면 코드가 더 깔끔해진다.</p>
<ul>
<li>N(다)에 넣기 : Member에 메서드 추가</li>
</ul>
<pre><code class="language-java">    @ManyToOne
    @JoinColumn(name=&quot;TEAM_ID&quot;)
    @Setter(value=AccessLevel.NONE) // lombok에서 자동 setter를 막는다.
    private Team team;

    // 일방적인 setter의 형태가 아니면 매서드 이름을 바꿔준다.
    // 추후 코드르 봤을때 단순 setter 작업이 아닌 중요한 작업을 진행하는지를 파악할 수 있다.
    public void changeTeam(Team team) {
        this.team = team;
        // this : 나 자신의 인스턴스(객체)
        team.getMember().add(this);
        // team.getMember().add(member); 를 여기서 만듬
    }</code></pre>
<ul>
<li>1(일)에 넣기 : Team에 메서드 추가</li>
</ul>
<pre><code class="language-java"> // 다대일, Team에는 여러 Member가 있기에 배열로 나타낸다. 일대다
    // mappedBy = &quot;team&quot;은 Member에있는 Private Team team에 객체이름
    @OneToMany(mappedBy = &quot;team&quot;)
    private List&lt;Member&gt; member
        = new ArrayList&lt;&gt;();

    public void addMember(Member member) {
        member.setTeam(this);
        this.member.add(member);
    }</code></pre>
<h1 id="7-양방향-매핑-정리">7. 양방향 매핑 정리</h1>
<ul>
<li>단방향 매핑만으로도 이미 연관관계 매핑은 완료</li>
<li>양방향 매핑은 반대 방향으로 조회 기능이 추가된 것 뿐</li>
<li>양방향 사용 이유 : JPQL에서 역방향으로 탐색할 일이 많기 때문</li>
<li>단방향 매핑을 잘 하고, 양방향은 필요할 때 추가해도 됨
(테이블에 영향을 주지 않음)</li>
</ul>
<h1 id="8-연관관계의-주인을-정하는-기준">8. 연관관계의 주인을 정하는 기준</h1>
<ul>
<li>비지니스 로직을 기준으로 연관관계 주인을 선택하면 안됨</li>
<li>연관관계의 주인은 외래키의 위치를 기준으로 정해야함<blockquote>
<p>즉 외래키가 있는 테이블이 오너이므로 헤깔리지 말자.</p>
</blockquote>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/4d5ebf68-a16d-4700-ad9b-5811279acb91/image.png" alt=""></p>
<p>startDate와 endDate는 하나의 묶음, city,street,zipcode를 한묶음으로 볼 수 있다.<img src="https://velog.velcdn.com/images/kyj-8754/post/88d633d4-5a5c-4535-949a-dcc74a80b5fb/image.png" alt=""></p>
<p>그렇다면 자바에서 했던 객체지향을 할 수있지않을까? 이러한 접근을 임베디드로 하는것이다.</p>
<h1 id="9-임베디드-타입">9. 임베디드 타입</h1>
<ul>
<li>새로운 값 타입을 직접 정의할 수 있다.</li>
<li>JPA는 임베디드 타입(embedded type)이라고 함</li>
<li>주로 기본 값 타입을 모아서 만들어서 복합값 타입이라고도 함</li>
</ul>
<h2 id="9-1-jpa에서-임베디드-타입-사용법">9-1. JPA에서 임베디드 타입 사용법</h2>
<ul>
<li>@Embeddable    : 값 타입을 정의하는 곳에 표시</li>
<li>@Embedded    : 값 타입을 사용하는 곳에 표시</li>
<li>기본 생성자 필수 </li>
</ul>
<pre><code class="language-java">    @Embedded
    private Period period;
    @Embedded
    private Address address;</code></pre>
<pre><code class="language-java">@Embeddable
@Getter @Setter
public class Period {

    private LocalDateTime startDate;
    private LocalDateTime endDate;

    // 테스트를 위해 파라미터가 있는 생성자
    public Period(LocalDateTime startDate, LocalDateTime endDate) {
        super();
        this.startDate = startDate;
        this.endDate = endDate;
    }

    public Period() {}
}</code></pre>
<p>두번째 코드처럼 하나의 클래스를 만들어 @Embeddable 을 붙여주고 @Embedded를 선언하여 따로 관리가 가능하다. DB테이블에서는 달라진 점이없으나, 개발자시점에서는 관리하기 매우 용이해진다.</p>
<h2 id="9-2-임베디드-타입의-장점">9-2. 임베디드 타입의 장점</h2>
<ul>
<li>재사용</li>
<li>높은 응집도</li>
<li>Period.isWork() 처럼 해당 값 타임만 사용하는 의미있는 메서드를 만들 수 있다. (객체지향적 설계가 가능하다)</li>
<li>임베디드 타입을 포함한 모든 값 타입은, 값 타입을 소유한 엔티티 생명주기에 의존한다.</li>
</ul>
<h2 id="9-3-임베디드-타입과-테이블-매핑">9-3. 임베디드 타입과 테이블 매핑</h2>
<ul>
<li>임베디드 타입은 엔티티의 값일 뿐이다.</li>
<li>임베디드 타입을 사용하기 전과 후에 매핑하는 테이블은 같다</li>
<li>객체와 테이블을 아주 세밀하게 매핑하는것이 가능</li>
<li>잘 설계한 ORM 애플리케이션은 매핑한 테이블의 수보다 클래스의 수가 더 많다.</li>
</ul>
<h2 id="9-4-attributeoverride--속성-재정의">9-4. @AttributeOverride : 속성 재정의</h2>
<ul>
<li><p>한 엔티티에서 같은 값 타입을 사용하려면 컬럼 명이 중복</p>
</li>
<li><p>@AttributeOverride, @AttributeOverrides를 사용해서 컬럼명 속성을 재 정의</p>
<blockquote>
<p>자바의 오버라이드 속성을 기억한다면 별 어려움이 없다. 즉 기존에 가지고 있는 변수타입을 크게 해치지 않는 선에서 변수명만 덧씌워준다</p>
</blockquote>
</li>
</ul>
<h2 id="9-5-객체-타입의-한계">9-5. 객체 타입의 한계</h2>
<ul>
<li>항상 값을 복사해서 사용하면 공유 참조로 인해 발생하는 부작용을 피할 수 있다.</li>
<li>문제는 임베디드 타입처럼 직접 정의한 값 타입은 자바의 기본 타입이아 아니라 객체 타입이다. <ul>
<li>자바 기본 타입에 값을 대입하면 값을 복사한다. </li>
</ul>
</li>
<li>객체 타입은 참조 값을 직접 대입하는 것을 막을 방법이 없다.</li>
<li>객체의 공유 참조는 피할 수 없다.</li>
</ul>
<h1 id="10-객체지향-쿼리-언어jpql">10. 객체지향 쿼리 언어(JPQL)</h1>
<h2 id="10-1-jpa는-다양한-쿼리-방법을-지원">10-1. JPA는 다양한 쿼리 방법을 지원</h2>
<ul>
<li>JPQL</li>
<li>QueryDSL</li>
<li>JPA Criteria</li>
<li>네이티브 SQL</li>
<li>JDBC API 직접 사용</li>
<li>MyBatis</li>
<li>SpringJdbcTemplate</li>
</ul>
<h2 id="10-2-jpql-소개">10-2. JPQL 소개</h2>
<ul>
<li>단순한 조회 : EntityManager.find();
 -&gt; 나이가 18살 이상인 회원을 모두 검색하고 싶다면?</li>
</ul>
<h2 id="10-3-jpql이란">10-3. JPQL이란</h2>
<p>Java Persistence Query Language 의 약자, JPA를 사용하면 엔티티 객체를 중심으로 개발하게 되는데. 여기서 걸리는 점이 쿼리 문이다. </p>
<ul>
<li>검색을 할 때도 테이블이 아닌 엔티티 객체를 대상으로 검색</li>
<li>모든 DB데이터를 객체로 변환해서 검색하는 것은 불가능</li>
<li>JPA는 SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어를 제공</li>
<li>SQL문법이 유사, select, from, where, group by, having, join 지원</li>
<li>JPQL은 엔티티 객체를 대상으로 쿼리 작성</li>
<li>SQL은 데이터 베이스 테이블을 대상으로 쿼리 작성</li>
<li>JPQL을 한마디로 정의하면 객체 지향 SQL</li>
</ul>
<h2 id="10-4-jpql-문법">10-4. JPQL 문법</h2>
<ul>
<li>대소문자 구분한다.
  -&gt; 엔티티와 속성은 대소문자 구분, JPQL 키워드는 구분하지 않는다.</li>
<li>엔티티 이름
  -&gt; 테이블명 대신 엔티티명을 사용</li>
<li>@Entity(name=&quot;...&quot;) 이러면 class명 대신, name에 지정한 이름이 들어간다. (기본값을 사용하는것을 추천한다.)</li>
<li>별칭은 필수
 -&gt; JPQL은 별칭이 필수
 -&gt; AS 예약어는 생략 가능</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Boot 10일차]]></title>
            <link>https://velog.io/@kyj-8754/Spring-Boot-10%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@kyj-8754/Spring-Boot-10%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Tue, 10 Oct 2023 07:34:00 GMT</pubDate>
            <description><![CDATA[<h1 id="1-영속성-컨텍스트">1. 영속성 컨텍스트</h1>
<h2 id="1-1-엔티티-등록---트랜잭션을-지원하는-쓰기-지연">1-1. 엔티티 등록 - 트랜잭션을 지원하는 쓰기 지연</h2>
<pre><code class="language-java">    EntityManager em = emf.createEntityManager();
    EntityTransaction ts = em.getTransaction();
    ts.begin(); // 트랜잭션 시작

    em.persist(memberA);
    em.persist(memberB);
    // 여기까지 InsertSQL을 데이터베이스에 보내지 않는다.

    // 커밋하는 순간 데이터베이스에 Insert SQL을 보낸다 즉 flush()가 발생
    ts.commit(); // 트랜잭션 커밋</code></pre>
<h2 id="1-2-엔티티-수정---변경감지dirty-checking">1-2. 엔티티 수정 - 변경감지(Dirty Checking)</h2>
<pre><code class="language-java">    EntityManager em = emf.createEntityManager();
    EntityTransaction ts = em.getTransaction();
    ts.begin(); // 트랜잭션 시작

    // 엔티티 조회
    Member memberA = em.find(Member.class, &quot;memberA&quot;);    

    // 엔티티 데이터 수정
    memberA.setUsername(&quot;admin&quot;);
    memberA.setAge(20);

    // em.update(memberA); 이런 코드는 없다.

    ts.commit(); // 트랜잭션 커밋</code></pre>
<p>-&gt; flush() 가 호출되는 시점에 Entity와 스냅샷을 전부 비교 후(최적화 된 알고리즘으로 진행)변경이 된것을 감시(Dirty Checking)한 후에 update 쿼리를 작성 후, update쿼리를 날린다.</p>
<p>캐시에 Entity이외에 스냅샷(사본같은 개념)에 있는 데이터와 비교하여 값이 다를 경우 쓰기지연 SQL저장소에 자동으로 update를 날려줌</p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/c50362af-6bee-4f6e-ae0b-dec99a28b289/image.png" alt=""></p>
<h2 id="1-3-엔티티-삭제">1-3. 엔티티 삭제</h2>
<pre><code>// 삭제 대상 엔티티 조회
Member memberA = em.find(member.class, &quot;memberA&quot;);

em.remove(memberA);</code></pre><p>저번 포스팅의 복기파트로 보면 된다.</p>
<h1 id="2-플러시flush">2. 플러시(flush)</h1>
<h2 id="2-1-플러시-발생">2-1. 플러시 발생</h2>
<ul>
<li>영속성 컨텍스트의 변경 내용을 데이터베이스에 반영</li>
<li>변경감지</li>
<li>수정된 엔티티 쓰기 지연 SQL 저장소에 등록</li>
<li>쓰기 지연 SQL저장소의 쿼리를 데이터베이스에 전송
(등록, 수정, 삭제 쿼리)</li>
</ul>
<h2 id="2-2-영속성-컨텍스트를-플러시-하는-방법">2-2. 영속성 컨텍스트를 플러시 하는 방법</h2>
<ul>
<li>트랜잭션 커밋    -&gt; 플러시 자동 호출</li>
<li>em.flush()     -&gt; 직접호출</li>
<li>jpql 쿼리 실행     -&gt; 플러시 자동 호출</li>
</ul>
<blockquote>
<p>jpql 쿼리 실행은 이번 포스팅에는 언급하지않는다.</p>
</blockquote>
<h2 id="2-3-플러시-주요-사항">2-3. 플러시 주요 사항</h2>
<ul>
<li>영속성 컨텍스트를 비우지 않음</li>
<li>영속성 컨텍스트의 변경 내용을 데이터베이스에 동기화</li>
<li>트랜잭션이라는 작업 단위가 중요 -&gt; 커밋 직전에만 동기화 하면 됨</li>
<li>JPA는 동시성, 데이터를 맞추거나 등을 모두 트랜잭션에 위임한다.</li>
</ul>
<h2 id="2-4-준영속-상태">2-4. 준영속 상태</h2>
<ul>
<li>영속상태 -&gt; 준영속 상태</li>
<li>준영속 : 영속 상태의 엔티티가 영속성 컨텍스트에서 분리(detached)</li>
<li>영속성 컨텍스트가 제공하는 기능을 사용하지 못함</li>
</ul>
<h2 id="2-5-준영속-상태로-만드는-방법알아만-둘것">2-5. 준영속 상태로 만드는 방법(알아만 둘것)</h2>
<ul>
<li>em.detach(entity);
: 특정 엔티티만 준영속 상태로 전환</li>
<li>em.clear();
: 영속성 컨텍스트를 완전히 초기화</li>
<li>em.close();
: 영속성 컨텍스를 종료</li>
</ul>
<h1 id="3-엔티티-매핑-소개">3. 엔티티 매핑 소개</h1>
<ul>
<li>객체와 테이블 매핑 : @Entity, @Table</li>
<li>필드와 컬럼 매핑 : @Column</li>
<li>기본 키 매핑 : @Id( Id = 컬럼 이름)</li>
<li>연관관계 매핑 : @ManyToOne, @JoinColumn (ManyToOne이 중요)</li>
</ul>
<h2 id="3-1-객체와-테이블-매핑---entity">3-1. 객체와 테이블 매핑 - @Entity</h2>
<ul>
<li>@Entity가 붙은 클래스는 JPA가 관리하는 엔티티라 한다.</li>
<li>JPA를 사용해서 테이블과 매핑할 클래스는 @Entity 필수
 -&gt; 기본 생성자 필수(파라미터가 없는 Public 또는 protected 생성자)
 -&gt; final 클래스, euum, interface, inner 클래스 사용 불가
 -&gt; 저장할 필드 final 사용x<h2 id="3-2-entity-속성-정리">3-2. @Entity 속성 정리</h2>
</li>
<li>속성 : name
 -&gt; JPA에서 사용할 엔티티 이름을 지정
 -&gt; 기본값 : 클래스 이름을 그대로 사용한다.
 -&gt; 같은 클래스 이름 없으면 가급적 기본값 사용하는걸 지향한다.</li>
</ul>
<h2 id="3-3-table">3-3. @Table</h2>
<ul>
<li>엔티티와 매핑할 테이블 지정</li>
<li>name : 매핑할 테이블 이름(엔티티 이름을 사용)</li>
<li>catalog : 데이터 베이스 catalog매핑</li>
<li>schema : 데이터베이스 schema 매핑</li>
<li>uniqueConstraints : DDL 생성시에 유니크 제약 조건 생성</li>
</ul>
<h1 id="4-데이터베이스-스키마-자동생성">4. 데이터베이스 스키마 자동생성</h1>
<ul>
<li>hibernate.hbm2ddl.auto &lt;- xml파일에 넣는 문구<ul>
<li>create        : 기존 테이블 삭제 후 다시 생성(Drop + Create)</li>
<li>create-drop    : create와 같으나 종료 시점에 Drop</li>
<li>update        : 변경문만 반영(운영 DB에는 사용하면 안됨)</li>
<li>validate        : 엔티티와 테이블이 정상 매핑되었는지만 확인</li>
<li>none            : 사용하지 않음
=&gt; 데이터베이스 방언별로 달라진다.</li>
</ul>
</li>
</ul>
<ul>
<li>운영 장비에는 절대 create, create-drop, update 사용하면 안됨</li>
<li>개발 초기 단계는 create 또는 update</li>
<li>테스트 서버는 update 또는 validate</li>
<li>스테이징과 운영 서버는 validate 또는 none을 주로 사용한다.</li>
</ul>
<h1 id="5-ddl-생성-기능">5. DDL 생성 기능</h1>
<ul>
<li><p>제약조건 추가 : 회원 이름은 필수, 10자 초과 x
 -&gt; @Column(nullable==false, length=10)</p>
<ul>
<li>유니크 제약 조건 추가</li>
<li><blockquote>
<p>@Table(uniqueConstraints={
@UniqueConstraint(
   name=&quot;NAME_AGE_UNIQUE&quot;,
   columnNames={&quot;NAME&quot;,&quot;AGE&quot;}
   )
})</p>
</blockquote>
</li>
</ul>
</li>
</ul>
<p>=&gt; DDL 생성 기능은 DDL을 자동 생성할 때만 사용되고 JPA의 실행 로직에는 영향을 주지 않는다.</p>
<h1 id="6-필드와-컬럼-매핑">6. 필드와 컬럼 매핑</h1>
<ul>
<li>@Column    : 컬럼 매핑</li>
<li>@Temporal    : 날짜 타입 매핑</li>
<li>@Enumerated    : enum 타입 매핑</li>
<li>@Lob        : BLOB, CLOB 매핑</li>
<li>@Transient    : 특정 필드를 컬럼에 매핑하지 않음 (매핑 무시)</li>
</ul>
<blockquote>
<p>@Transient은 자바안에서만 만들고, DB에는 쿼리문을 날리지 않는 말그대로 test용 더미 컬럼으로 보면 된다.</p>
</blockquote>
<h1 id="7-column">7. @Column</h1>
<ul>
<li>name : 필드와 매핑할 테이블의 컬럼 이름</li>
<li>nullable : null값의 허용 여부를 설정한다. false로 설정하면 DDL 생성시에 not null 제약조건이 붙는다</li>
<li>unique : @Table uniqueConstraints 와 같지만 한 컬럼에 간단히 유니크 제약 조건을 걸 때 사용한다.</li>
<li>length : 문자 길이 제약조건, String 타입에만 사용</li>
</ul>
<pre><code class="language-java">@Entity
@Getter @Setter
public class Team {

    @Id @Column(name=&quot;TEAM_ID&quot;)
    @GeneratedValue
    private Long id;
    private String name;
}</code></pre>
<blockquote>
<p>id를 pk로 설정하고, 해당 클래스에서는 id라고 변수이름을 설정 하였지만 DB에 insert할 경우 TAEM_ID로 값이 저장되는 어노테이션을 선언해주는모습, @GeneratedValue는 serquence를 만드는 어노테이션이다. </p>
</blockquote>
<h1 id="8-기본키-매핑-방법">8. 기본키 매핑 방법</h1>
<ul>
<li>직접 할당하는 방법     : @Id 만 사용</li>
<li>자동 생성 방법         : @GeneratedValue -&gt; 전략
 -&gt; IDENTITY
 : 데이터베이스에 위임, MYSQL, SQL Server, DB2
 -&gt; SEQUENCE
 : 데이터베이스 시퀀스 오브젝트 사용, ORACLE, PostgreSQL, DB2, H2
 @SequenceGenerator 필요
 -&gt; TABLE
 : 키 생성용 테이블 사용, 모든 DB에서 사용
 @TableGenerator 필요
 -&gt; AUTO
 : 방언에 따라 자동지정, 기본값
 -&gt; AUTO는 DB방언에 맞춰서 IDENTITY, SEQEUNCE, TABLE 3개의 방식중 하나가 선택이 된다.</li>
</ul>
<h1 id="9-sequencegenerator">9. @SequenceGenerator</h1>
<ul>
<li>name        : 식별자 생성기 이름 , name은 generator로 자바단에서 매핑이 된다.</li>
<li>sequenceName : 데이터베이스에 등록되어 있는 시퀀스 이름</li>
<li>initialValue    : DDL생성 시에만 사용됨, 시퀀스 DDL을 생성할 때 처음 시작하는 수를 지정한다.(기본값 1)</li>
<li>allocationSize : 시퀀스 한 번 호출에 증가하는 수(데이터베이스 시퀀스 값이 하나씩 증가하도록 설정되어 있으면 이 값을 반드시 1로 설정해야 한다. 기본값 = 50)</li>
<li>catalog, schema : 데이터 베이스 catalog, schema(오라클을 쓰는 단계에서는 신경쓸 필요 가 없음, MySQL경우 DataBase(schema)생성을 하도록 한다. (오라클에서는 XE가 스키마))</li>
</ul>
<h1 id="10-table-전략---사용-빈도가-낮음">10. TABLE 전략 -&gt; 사용 빈도가 낮음</h1>
<ul>
<li>키 생성 전용 테이블을 하나 만들어서 데이터베이스 시퀀스를 흉내내는 전략</li>
<li>장점 : 모든 데이터베이스에 적용가능</li>
<li>단점 : 효율이 낮음 </li>
</ul>
<h1 id="11-연관관계-매핑-기초">11. 연관관계 매핑 기초</h1>
<ul>
<li>객체와 테이블 연관관계의 차이를 이해</li>
<li>객체의 참조와 테이블의 외래키를 매핑</li>
<li>용어 이해 
 -&gt; 방향(Direction) : 단방향, 양방향
  -&gt; 다중성(Multiplicity) : 다대일(N:1), 일대다(1:N), 일대일(1:1), 다대다(N:M)
 -&gt; 연관관계의 주인(Owner): 객체 양방향 연관관계는 관리 주인이 필요</li>
</ul>
<h1 id="12-단방향-연관관계">12. 단방향 연관관계</h1>
<ul>
<li>일방적으로 한 방향으로만 조인이 가능한 상태</li>
</ul>
<p>두 개의 객체 테이블을 만들 경우, DB에서는 외래키 선언을 통한 두 테이블간에 data값을 join하고, 그 값을 불러올 수 있다.</p>
<p>그러나 자바단에서는 외래키 선언을 하기 위해서는 @ManyToOne이라는 어노테이션을 지정해야 외래키 지정을 할 수있다. 또한 한 객체에만 선언하면 다른 객체에서는 해당 키값을 이용해 매핑을 할 수 없다.
즉, 한 테이블에서는 가능하지만 다른 테이블에서는 조인할 수 없는 상태를 단방향 연관 관계라 할 수 있다.</p>
<p>결론은 DB에서는 외래키 설정을 하면 기본적으로 양방향 성격을 띄지만 자바에서는 경우에 따라서 단방향이란걸 인지해야 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Boot 9일차]]></title>
            <link>https://velog.io/@kyj-8754/Spring-Boot-9%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@kyj-8754/Spring-Boot-9%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Tue, 10 Oct 2023 04:51:02 GMT</pubDate>
            <description><![CDATA[<p><code>갑자기 9일차 가는 이유는 중간에 계속 기존에 한 것 연장선으로 갔기때문에 9일차로 변경이 되었습니다.</code></p>
<h1 id="1-영속성-관리---jpa-내부-구조">1. 영속성 관리 - JPA 내부 구조</h1>
<h2 id="1-1-영속성-컨텍스트">1-1. 영속성 컨텍스트</h2>
<ul>
<li>JPA를 이해하는데 가장 중요한 용어</li>
<li>엔티티를 영구 저장하는 환경이라는 뜻</li>
<li>EntityManager.persist(entity);
 -&gt; &quot;entity&quot;에 들어가는 객체(Team, Member..)등을 DB에 저장하는것처럼 보이지만 좀 더 깊은 의미가 있다.</li>
<li>DB에 저장하는게 아니라 영속성 컨텍스트라는 곳에 저장하는것</li>
<li>영속성 컨텍스트는 논리적인 개념</li>
<li>눈에 보이지 않는다.</li>
<li>엔티티 매니저를 통해 영속성 컨텍스트에 접근한다.</li>
<li>엔티티 매니저를 호출하면 생성하는 영속성 컨텍스트 1:1 생성된다.</li>
</ul>
<h2 id="1-2-엔티티의-생명주기">1-2. 엔티티의 생명주기</h2>
<ul>
<li>비영속(new/trasient)
 -&gt; 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태</li>
<li>영속(managed)
 -&gt; 영속성 컨텍스트에 관리되는 상태</li>
<li>준영속(detached)
 -&gt; 영속성 컨텍스트에 저장되었다가 분리된 상태</li>
<li>삭제(removed)
 -&gt; 삭제된 상태</li>
</ul>
<h2 id="1-3-비영속">1-3. 비영속</h2>
<ul>
<li><p>JPA와 전혀 상관없는 상태</p>
<p> Member member = new Member();
 member.setId(&quot;member1&quot;);
 member.setUsername(&quot;회원1&quot;);</p>
</li>
</ul>
<p>위 객체는 아직 컨텍스트에 영속되어 있지 않은 상태로 보면 된다.</p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/c78886b3-44b7-475f-8ac0-973c904923c5/image.png" alt=""></p>
<p>아직 member라는 객체가 컨텍스트에는 포함되지 않은 상태이다.</p>
<h2 id="1-4-영속">1-4. 영속</h2>
<ul>
<li><p>JPA에 속해있는 상태</p>
<p> Member member = new Member();
 member.setId(&quot;member1&quot;);
 member.setUsername(&quot;회원1&quot;);</p>
<p> EntityManager em = emf.createEntityManager();
 em.getTransaction().begin();</p>
<p> // 객체를 영속
 em.persist(member);</p>
</li>
</ul>
<p>em.persist(member);을 사용함으로써 컨텍스트에 영속을 시켜준다.</p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/c78886b3-44b7-475f-8ac0-973c904923c5/image.png" alt=""></p>
<h2 id="1-5-준영속">1-5. 준영속</h2>
<ul>
<li><p>회원 엔티티를 영속성 컨텍스트에서 분리시키는것</p>
<p> em.detach(member);</p>
</li>
</ul>
<h2 id="1-6-삭제">1-6. 삭제</h2>
<ul>
<li><p>객체 삭제를 요청</p>
<p> em.remove(member);</p>
</li>
</ul>
<p>이후 persist를 다시 선언 해 주어야만 다시 영속이 가능하다.</p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/25c6eeb9-166b-4ad2-ab4f-f0603d38650c/image.png" alt=""></p>
<p>사실 현재 commit을 선언하지 않았기 때문에 DB에는 SQL을 날리지 않은걸로 보면 되는 상황이다.
즉, Managed 상태만 보고있다고 보면 된다.</p>
<p>commit문을 날리게 되면 flush()가 포함되어있어, SQL을 insert하게 된다.</p>
<h2 id="1-7-영속성-컨텍스트의-이점">1-7. 영속성 컨텍스트의 이점</h2>
<ul>
<li>1차캐시</li>
<li>동일성(identity)보장</li>
<li>트랜잭션을 쓰기 지연(transactional write-behind)</li>
<li>변경 감지(Dirty Checking)</li>
<li>지연 로딩(Lazy Loading)</li>
</ul>
<h2 id="1-8-엔티티-조회---1차-캐시">1-8. 엔티티 조회 - 1차 캐시</h2>
<ul>
<li>영속성 컨텍스트와 식별자 값
 -&gt; 엔티티를 식별자 값(@id로 테이블의 기본 키와 매핑)으로 구분
 -&gt; 영속 상태는 식별자 값이 반드시 있어야 한다.
 -&gt; 식별자 값이 없으면 예외 상황 </li>
<li>영속성 컨텍스트와 데이터베이스 저장
 -&gt; JPA는 보통 트랜잭션을 커밋하는 순간 영속성 컨텍스트에 새로 저장된 엔티티를 데이터 베이스에 반영, 즉 플러시(flush)메서드가 실행된다는 뜻</li>
</ul>
<p>컨텍스트 안에 key와 value값을 영속한 공간을 1차 캐시라고 하는데, 이때는 아직 commit을 날리기 전이므로 DB에는 값이 저장되어 있지 않다. </p>
<p>이 상태에서 엔티티매니저를 통해 find메서드를 선언하면, DB보다 1차캐시에서 값을 먼저 찾아 해당key에 value값이 있다면 보여준다. </p>
<p>만약 값이 1차캐시에 저장되어 있는 key가 없다면 DB에서 값을찾아 1차캐시로 return하여 저장하고 select한다.</p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/b7e38ab6-8e70-433f-a413-1567452cc418/image.png" alt=""></p>
<blockquote>
<p>1차캐시에 값이 있기 때문에 DB를 거치지 않고 바로 member라는 value를 반환한다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/5bfcb175-1f79-44af-b189-e4ca26769598/image.png" alt=""></p>
<blockquote>
<p>1차 캐시에 값이 없는 member2 를 찾았기 때문에 DB에서 조회한 후 1차 캐시에 저장하고 그 값을 반환하는 모습을 볼 수 있다.</p>
</blockquote>
<pre><code class="language-java">        EntityManagerFactory emf 
            = Persistence.createEntityManagerFactory(&quot;hello&quot;);
        EntityManager em = emf.createEntityManager();

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

        Member member = new Member();

        // 추가
        member.setId(5L);
        member.setName(&quot;UserC&quot;);

        //회원 조회
        Member findMember = em.find(Member.class, 2L);
        System.out.println(&quot;findMember.id : &quot; + findMember.getId());
        System.out.println(&quot;findMember.name : &quot; + findMember.getName());

        //회원 수정
        findMember.setName(&quot;SuperUser&quot;);

        em.persist(member);


        tx.commit();
        em.close();
        emf.close();</code></pre>
<blockquote>
<p>EntityManager를 만들면서, 트랜잭션을 시작하고, 미리 만들어둔 Member객체를 꺼내와, 데이터를 넣고 persist로 영속성 컨텍스트에 추가한 뒤, commit을 날려 DB에 저장하고 close로 트랜잭션과 엔티티매니저를 닫아주는 코드의 전체적인 모습이다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[스프링 부트 중간 정리]]></title>
            <link>https://velog.io/@kyj-8754/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-%EC%A4%91%EA%B0%84-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@kyj-8754/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-%EC%A4%91%EA%B0%84-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Wed, 27 Sep 2023 08:45:59 GMT</pubDate>
            <description><![CDATA[<p>Spring Boot 공부용 </p>
<ol>
<li><p>FrameWork란?</p>
<ul>
<li>뼈대나 근간을 이루는 코드들의 묶음</li>
<li>프로그래밍의 기본틀</li>
</ul>
</li>
<li><p>라이브러리란?</p>
<ul>
<li>자주 사용되는 코드를 정리한 집합체</li>
</ul>
</li>
<li><p>DispatcherServlet</p>
<ul>
<li>스프링의 프론트 컨트롤러를 의미</li>
<li>MVC패턴의 핵심, DispatcherServelt -&gt; FrameworkServlet -&gt; HttpServletBean -&gt; HttpServlet 순으로 상속</li>
</ul>
</li>
</ol>
<p>컨트롤러 : 웹 mvc의 컨트롤러 역할</p>
<p>서비스 : 핵심 비즈니스 로직 구현</p>
<p>레파지토리 : 데이터베이스에 접근</p>
<p>도메인 : 비즈니스 도메인 객체, 예)회원, 주문, 쿠폰 등등</p>
<p>컨트롤러는 서비스에 의존하고, 서비스는 레파지토리에 의존한다.(중요)</p>
<ol start="4">
<li>Container, Bean</li>
</ol>
<ul>
<li><p>스프링에는 객체를 자동으로 추가하고 관리하는데 이때 객체는 Bean으로 명칭한다.
Bean을 담아 관리하는 걸 컨테이너(Container)라고 부른다.</p>
</li>
<li><p>자동으로 담아주기도 하지만 @Autowired를 사용하면 수동으로도 가능하다.</p>
</li>
</ul>
<ol start="5">
<li><p>IoC(제어 역전, Inversion of Control)
위에 컨테이너와 빈을 설명한것 처럼 개발자가 직접 라이브러리를 만들어서 관리하는게아닌, 외부 프로그램에 의해서 빈이 컨테이너 안에서 관리되어지는 형태 즉, 개발자가아닌 프로그램이 제어하는걸 제어 역전이라 부른다)</p>
</li>
<li><p>DI(Dependency Injection)
의존성 주입, 개발자가 임의로 의존성을 부여하는걸 의미한다. 
방식으로는</p>
</li>
<li><p>Field Injection(필드 주입)</p>
</li>
<li><p>Setter Injection(수정자 주입)</p>
</li>
<li><p>Constructor Injection(생성자 주입)
이 있다.</p>
</li>
<li><p>SOLID
객체 지향 설계의 5가지 원리</p>
</li>
</ol>
<p>-객체지향?  Object-Oriented Programming, OOP,객체지향 프로그래밍 이라고 불리며, 필요한 데이터를 추상화 시켜 상태와 행위를 가진 객체를 만들고, 그 객체들간 상호작용을 통해 로직을 구성하는걸 의미한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Boot 4일차]]></title>
            <link>https://velog.io/@kyj-8754/Spring-Boot-4%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@kyj-8754/Spring-Boot-4%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Mon, 25 Sep 2023 07:07:03 GMT</pubDate>
            <description><![CDATA[<h1 id="1-기본-객체들">1. 기본 객체들</h1>
<ul>
<li>타임리프는 기본객체들을 제공한다.<ul>
<li>${#request}</li>
<li>${#response}</li>
<li>${#session}</li>
<li>${#servletContext}</li>
<li>${#locale}</li>
</ul>
</li>
</ul>
<h1 id="2-연산">2. 연산</h1>
<ul>
<li>비교연산 : HTML 엔티티를 사용해야하는 부분을 주의
<code>&gt;(gt), &lt;(lt), &gt;=(ge), &lt;=(le), !(not),  ==(eq), !=(neq, ne)</code></li>
</ul>
<h1 id="3-속성-값-설정">3. 속성 값 설정</h1>
<ul>
<li>타임리프 태그 속성</li>
<li>타임리프는 주로 HTML태그에 <code>th:*</code> 속성을 지정하는 방식으로 동작한다. <code>th:*</code>로 속성을 적용하면 기존 속성을 대체한다. 기존 속성이 없으면 새로만든다.
th를 사용한다면 name, class, id 같은 속성에 값을 새로 설정해줄 수 있다.</li>
</ul>
<h1 id="4-반복-상태-유지">4. 반복 상태 유지</h1>
<ul>
<li><p><code>&lt;tr th:each=&quot;user,userStat:${users}&quot;&gt;</code></p>
<blockquote>
<p>each로 for문처럼 반복루프를 설정할 수 있다.</p>
</blockquote>
</li>
<li><p>반복된 두번째 파라미터를 설정해서 반복의 상태를 확인할 수 있다.</p>
</li>
<li><p>지정한 변수명(user) + Stat -&gt; userStat =&gt; 생략가능</p>
<pre><code class="language-html">&lt;table border=&quot;1&quot;&gt;
    &lt;tr&gt;
        &lt;th&gt;count&lt;/th&gt;
        &lt;th&gt;username&lt;/th&gt;
        &lt;th&gt;age&lt;/th&gt;
        &lt;th&gt;etc&lt;/th&gt;
    &lt;/tr&gt;
    &lt;!-- userStat : 현재 loop에 대한 상태를 알려준다. --&gt;
    &lt;tr th:each=&quot;user , userStat : ${users}&quot;&gt;
        &lt;td th:text=&quot;${userStat.count}&quot;&gt;count&lt;/td&gt;
        &lt;td th:text=&quot;${user.username}&quot;&gt;username&lt;/td&gt;
        &lt;td th:text=&quot;${user.age}&quot;&gt;age&lt;/td&gt;
        &lt;td&gt;
            index     = &lt;span th:text=&quot;${userStat.index}&quot;&gt;&lt;/span&gt;
            count     = &lt;span th:text=&quot;${userStat.count}&quot;&gt;&lt;/span&gt;
            size     = &lt;span th:text=&quot;${userStat.size}&quot;&gt;&lt;/span&gt;
            first    = &lt;span th:text=&quot;${userStat.first}&quot;&gt;&lt;/span&gt;
            last     = &lt;span th:text=&quot;${userStat.last}&quot;&gt;&lt;/span&gt;
            current = &lt;span th:text=&quot;${userStat.current}&quot;&gt;&lt;/span&gt;
        &lt;/td&gt;
    &lt;/tr&gt;
&lt;/table&gt;</code></pre>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/6a945081-68de-421b-a326-92ecdf28611a/image.png" alt=""></p>
<p>코딩을 넣은 결과 다음과 같은 결과가 나온다.
index는 0부터 시작하고 count는 1부터 시작하는 차이점이 있다는걸 잊지말자, current는 주소값을 받아오기에 현재 저렇게 표기가 된다.</p>
<h1 id="5-조건부-평가">5. 조건부 평가</h1>
<ul>
<li>타임리프의 조건식 if, unless(if의 반대) <blockquote>
<p>예를 들어  </p>
<pre><code>          &lt;td&gt;
         &lt;span th:text=&quot;${user.age}&quot;&gt;&lt;/span&gt;
         &lt;span th:text=&quot;&#39;미성년자&#39;&quot; 
         th:if=&quot;${user.age lt 20}&quot;&gt;&lt;/span&gt;
         &lt;span th:text=&quot;&#39;미성년자&#39;&quot; 
         th:unless=&quot;${user.age ge 20}&quot;&gt;&lt;/span&gt;
     &lt;/td&gt;</code></pre><p>라고 코딩을 완성했다면 age &lt; 20 을 if에서는 age &lt; 20으로 조건을걸어두면 되지만, unless는 반대로 해야하기 때문에 age &gt;= 20 으로 조건을 걸어두는게 보인다, 이상, 이하, 미만, 초과 도 고려해야하는 모습이다.</p>
</blockquote>
</li>
<li>타임리프는 해당 조건이 맞지 않으면 태그자체를 렌더링 하지 않는다.</li>
<li>해당 조건이 false인 경우 <code>&lt;span&gt;...&lt;/span&gt;</code> 부분 자체가 렌더링 되지 않고 사라진다</li>
</ul>
<h1 id="6-주석">6. 주석</h1>
<h2 id="6-1-표준-html-주석">6-1. 표준 HTML 주석</h2>
<ul>
<li>자바스크립의 표준 HTML주석은 타임리프가 렌더링 하지 않고, 그대로 남겨둔다.
<code>&lt;!-- --&gt;</code><h2 id="6-2-타임리프-파서-주석">6-2. 타임리프 파서 주석</h2>
</li>
<li>타임리프 파서 주석은 타임리프의 진짜 주석이다. 렌더링에서 주석부분을 제거한다.
<code>&lt;!--/* */--&gt;</code><h2 id="6-3-타임리프-프로토타입-주석">6-3. 타임리프 프로토타입 주석</h2>
<ul>
<li>타임리프 프로토타입 주석은 HTML주석에 약간의 구문을 더했다. HTML 파일을 웹
브라우저에서 그대로 열어보면 HTML 주석이기 때문에 이 부분이 웹 브라우저가 렌더링 하지 않는다.</li>
<li>타임리프의 렌더링을 거치면 이 부분이 정상 렌더링 된다.</li>
<li>쉽게 이야기 하면, HTML파일을 그대로 열어보면 주석처리가 되지만, 타임리프를 렌더링한 경우에만 보이는 기능이다.
<code>&lt;!--/*/  /*/--&gt;</code></li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/88edf85d-02d9-4674-8e53-de4b94d59839/image.png" alt=""></p>
<p>코딩을 이렇게 짜보고 결과를 확인해보자.
<img src="https://velog.velcdn.com/images/kyj-8754/post/2e1f27bb-4082-4e82-9af5-d5884cad0e47/image.png" alt=""></p>
<blockquote>
<p>도메인 결과</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/6a04faf1-f12f-4174-bf70-03100cd31b51/image.png" alt=""></p>
<blockquote>
<p>개발자 모드확인 결과.</p>
</blockquote>
<p>주석이 보이는것이 있고 그렇지 않은것이 있다.</p>
<h1 id="7-블록">7. 블록</h1>
<ul>
<li>HTML태그가 아닌 타임리프의 유일한 자체 태그이다.</li>
<li><a href="th:block">th:block</a></li>
<li>사용빈도가 높지는 않고 어쩔 수 없이 사용될 때가 있다.</li>
</ul>
<h1 id="여담">여담</h1>
<p>현재 전부를 설명하지 않았으므로 부족한 정보에 대해서는 다음 링크를 첨부한다.
  <a href="https://sweets1327.tistory.com/53">https://sweets1327.tistory.com/53</a> 
  <a href="https://bnzn2426.tistory.com/140">https://bnzn2426.tistory.com/140</a>
  이 링크가 잘 되어서 참고할만하다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Boot 3일차 ]]></title>
            <link>https://velog.io/@kyj-8754/Spring-Boot-3%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@kyj-8754/Spring-Boot-3%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Fri, 22 Sep 2023 08:49:24 GMT</pubDate>
            <description><![CDATA[<h1 id="1-http요청-파라미터">1. HTTP요청 파라미터</h1>
<blockquote>
<p>HTTP요청 데이터 조회 
 스프링이 얼마나 깔끔하고 효율적으로 요청 데이터를 전달하는지 방법을 알아보자</p>
</blockquote>
<h2 id="1-1-requestparam">1-1. @RequestParam</h2>
<ul>
<li>스프링이 제공하는 @RequestParam을 사용하면 요청 파라미터를 매우 편리하게 사용할 수 있다.</li>
</ul>
<pre><code class="language-java"> @RequestMapping(&quot;/request-param-v1&quot;)
    public void requestParamV1(HttpServletRequest request,
            HttpServletResponse response) throws IOException{
        String username = request.getParameter(&quot;username&quot;);
        int age = Integer.parseInt(request.getParameter(&quot;age&quot;));

        System.out.println(&quot;username : &quot; + username);
        System.out.println(&quot;age : &quot; + age);
        response.getWriter().write(&quot;ok&quot;);
    }</code></pre>
<p>해당 코드를 뜯어보자.. /request-param-v1 주소를 mapping하여 parameter값을 설정하고, 그 값을 저장하고 불러오도록 하는 아주 간단한 작업이다.</p>
<p>포트는 9090을 사용하고있다면 <code>http://localhost:9090/request-param-v1?username=자바&amp;name=20</code> 을 넣는다면 콘솔창에 username : 자바, age = 20이 나올것이고, html은 ok가 뜨는걸 확인할 수 있다.
여기서 @RequestParam 을 이용해서 더 간략하게 바뀌는걸 보여줄거다.</p>
<pre><code class="language-java"> @RequestMapping(&quot;/request-param-v2&quot;)
      public String requestParamV2(
              @RequestParam(&quot;username&quot;) String memberName,
              @RequestParam(&quot;age&quot;) int memberAge){
          System.out.println(&quot;username : &quot; + memberName);
          System.out.println(&quot;age : &quot; + memberAge);
          return &quot;ok2&quot;;
          // 특이점은 age처럼 int형인것들도 string이 아니라 int로 받아오기에 형변환할 필요가 없다</code></pre>
<p>변수 타입을 지정하던게 사라지는게 보이는가.. 심지어는 cast없이 int를 @RequestParam으로 받아온다...
심지어 HTTP에 이름이 지정한 변수와 이름이 같다면 파라미터를 잡아주는것도 생략이 가능해서 아래 코드처럼 된다</p>
<pre><code class="language-java">@RequestMapping(&quot;/request-param-v3&quot;)
    public String requestParamV3(
            @RequestParam String username,
            @RequestParam int age){
        System.out.println(&quot;username : &quot; + username);
        System.out.println(&quot;age : &quot; + age);
        return &quot;ok3&quot;;
    }</code></pre>
<p> 여기서 String, int같은 단순 변수면 그냥 변수선언도 안한다.</p>
<pre><code class="language-java"> @RequestMapping(&quot;/request-param-v4&quot;)
    public String requestParamV4(String username, int age){
        System.out.println(&quot;username : &quot; + username);
        System.out.println(&quot;age : &quot; + age);
        return &quot;ok4&quot;;
    }</code></pre>
<p>  <del>이정도면 사기치는정도</del></p>
<p>위 코드 들은 생긴게 달라보여도 모두 같은 값을 보여준다.
물론 이게 자바 스프링에 한정된 방식이긴하다.</p>
<p>그런데 이제 여기서 한가지 의문점이 생긴다. 바로 변수에 값이 없다면? 인데.. 그래서 1일차에 살짝 언급하고 지나간 required문을 사용한다. required문은 false면 값이 없어도 진행하고 ture면 값이 없으면 호출을 거부한다.</p>
<blockquote>
</blockquote>
<pre><code class="language-java">    @ResponseBody
    @RequestMapping(&quot;/request-param-required&quot;)
    public String requestParamRequired(
        @RequestParam(required = true) String username,
        @RequestParam(required = false)  Integer age){
        System.out.println(&quot;username : &quot; + username);
        System.out.println(&quot;age : &quot; + age);
        return &quot;required&quot;;
    }</code></pre>
<p>int는 값을 넣지 않으면 null값을 받아오기에 int가아니라 Integer를 선언해주는게 포인트, 갑자기 @ResponseBody가 생겨서 저게 뭐지 하시는분이있다면 저건 html로 넘어갈때 return에 쓰여진 값을 보여주게 하는 구문이라고 보시면 될것 같다.</p>
<p>그러면 이제 &quot;어? true면 이제 값이 없으면 무조건 빠꾸(?) 먹는건가 하시겠지만 그런 경우를 위해서 defaultValue가 존재한다.</p>
<pre><code class="language-java">@RequestParam(required = true, defaultValue = &quot;guest&quot;) String username,
@RequestParam(required = false, defaultValue = &quot;-1&quot;)  int age)</code></pre>
<p>이렇게 집어넣어주면 값을 부여하지 않더라도 defaultValue에 값이 대신 출력된다. 덤으로 age에 -1이 들어가기 때문에 Integer할 필요없이 int를 써줘도 된다.</p>
<p><em>사실 이렇게 DefaultValue를 선언 해 주면 required = true를 쓰는 의미도 사라진다... 어차피 기본값은 항상 존재하니까</em></p>
<h2 id="1-2modelattribute">1-2.@ModelAttribute</h2>
<p>전에 사용한 Model model를 기억하시는분이 있을지 모르겠다.. Map&lt;Key, Value&gt;랑 같은 방식인데 이게 어노테이션에도 있다.</p>
<pre><code class="language-java">    @ResponseBody
    @RequestMapping(&quot;/request-param-map&quot;)
    public String requestParamMap(
            @RequestParam Map&lt;String, Object&gt; paramMap){
        System.out.println(&quot;username : &quot; + paramMap.get(&quot;username&quot;));
        System.out.println(&quot;age : &quot; + paramMap.get(&quot;age&quot;));
        return &quot;map&quot;;
    }</code></pre>
<p>여기 이쁜(?) 코드가 있다. key값으로 username, age를 설정하고, 거기에 담겨서 위 주소로 보내면 value를 꺼내 보여준다.</p>
<pre><code class="language-java">package com.codingbox.core3.basic;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter @Setter @ToString
    public class HelloData {
    private String username;
    private int age;
}</code></pre>
<p>이렇게 HelloData라는 이름의 자바에 Getter, Setter를 설정해주고..</p>
<pre><code class="language-java">    @ResponseBody
    @RequestMapping(&quot;/request-attribute-v1&quot;)
    public String requestAttributeV1(
            @RequestParam String username,
            @RequestParam int age){
        HelloData helloData = new HelloData();
        helloData.setUsername(username);
        helloData.setAge(age);

        System.out.println(&quot;username : &quot; + helloData.getUsername());
        System.out.println(&quot;age : &quot; + helloData.getAge());
        return &quot;attr1&quot;;
    }</code></pre>
<p>컨트롤러쪽을 요로코롬 해준다면 이클립스, 자바에서흔히 쓰던 문구가 만들어진다.
그런데 여기에 <code>@ModelAttribute</code> 가 들어간다면?</p>
<pre><code class="language-java">    @ResponseBody
    @RequestMapping(&quot;/request-attribute-v2&quot;)
    public String requestAttributeV2(
            @ModelAttribute HelloData helloData){

        System.out.println(&quot;username : &quot; + helloData.getUsername());
        System.out.println(&quot;age : &quot; + helloData.getAge());
        System.out.println(&quot;helloData : &quot; + helloData.toString());
        return &quot;attr2&quot;;
    }</code></pre>
<p>어. @RequestParam이 없다.. setter설정이 없어졌다..</p>
<p>그런데 우리 아까 @RequestParam할 때 단순한 변수형은 생략했는데, 이녀석도 그게 가능하다</p>
<pre><code class="language-java">        @ResponseBody
        @RequestMapping(&quot;/request-attribute-v3&quot;)
        public String requestAttributeV3(HelloData helloData){
            System.out.println(&quot;username : &quot; + helloData.getUsername());
            System.out.println(&quot;age : &quot; + helloData.getAge());
            System.out.println(&quot;helloData : &quot; + helloData.toString());
            return &quot;attr3&quot;;
        }</code></pre>
<blockquote>
<p>@ModelAttribute도 없어진 모습</p>
</blockquote>
<p>사실 @ModelAttribute은 없애버리면 코드 리딩이 매우 불친절해져서 이 단계까지는 가지않는다..</p>
<p>그냥 이럴수 있다고만 보고 따라하지는 말자.</p>
<h1 id="2-thymeleaf타임리프-특징">2. thymeleaf(타임리프) 특징</h1>
<h2 id="2-1-서버-사이드-html-렌더링ssr">2-1. 서버 사이드 HTML 렌더링(SSR)</h2>
<ul>
<li>타임리프는 백엔드 서버에서(jsp처럼) HTML을 동적으로 렌더링 하는 용도로 사용된다.</li>
</ul>
<h2 id="2-2-넷츄럴-템플릿">2-2. 넷츄럴 템플릿</h2>
<ul>
<li>타임리프는 순수 HTML을 최대한 유지하는 특징이있다.</li>
<li>타임리프으로 작성한 파일은 HTML을 유지하기 때문에 웹 브라우저에서 파일을 직접 열어도 내용을 확인할 수 있고, 서버를 통해 뷰 템플릿을 거치면 동적으로 변경된 결과를 확인할 수 있다.</li>
<li>순수 HTML을 그대로 유지하면서 뷰 템플릿도 사용할 수 있는 타임리프의 특징을 네츄럴 템플릿(natural templates)라 한다.</li>
</ul>
<h2 id="2-3-스프링-통합-지원">2-3. 스프링 통합 지원</h2>
<ul>
<li>타임리프는 스프링과 자연스럽게 통합되고, 스프링의 다양한 기능을 편리하게 사용할 수 있게 지원한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Boot 2일차 (DB연결, MVC패턴)]]></title>
            <link>https://velog.io/@kyj-8754/Spring-Boot-2%EC%9D%BC%EC%B0%A8-DB%EC%97%B0%EA%B2%B0-MVC%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@kyj-8754/Spring-Boot-2%EC%9D%BC%EC%B0%A8-DB%EC%97%B0%EA%B2%B0-MVC%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Thu, 21 Sep 2023 07:52:53 GMT</pubDate>
            <description><![CDATA[<h1 id="1-스프링-컨테이너란spring-container">1. 스프링 컨테이너란(Spring Container)</h1>
<ul>
<li><p>스프링 컨테이너는 자바 객체의 생명 주기를 관리하며, 생성된 자바 객채들에게 추가적인 기능을 제공하는 역할을 한다. 여기서 말하는 자바 객체를 스프링에서는 <strong>빈(Bean)</strong>이라고 명칭한다.</p>
</li>
<li><p>개발자는 객체를 생성하고 소멸할 수 있는데, 스프링 컨테이너가 이 역할을 대신해 준다.
즉, 제어의 흐름을 외부에서 관리하는 것이다. 또한, 객체들 간의 의존관계를 스프링 컨테이너가
런타임 과정에서 알아서 만들어 준다.</p>
</li>
</ul>
<ul>
<li>스프링은 실행시 객체들을 담고있는 Container가 있다.</li>
</ul>
<blockquote>
<p>컨테이너에 넣을 빈을 설정하는 방법으로는 어노테이션이 있다. </p>
</blockquote>
<h1 id="2-membercontroller">2. MemberController</h1>
<ul>
<li>생성자에 <strong>@Autowired</strong> 가 있으면 스프링이 연관된 객체를 스프링 컨테이너에 찾아서 넣어준다.
이렇게 객체 의존관계를 외부에서 넣어주는 것을 DI(Dependency Injection), 의존성 주입이라 한다.</li>
</ul>
<ul>
<li><p>개발자가 직접 주입하는 형식이라면, @Autowired에 의해서 스프링이 주입해 준다.</p>
</li>
<li><p>생성자가 단일생성자에 한해 @Autowired를 붙이지 않아도 된다.</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/a11cd2a0-c204-4a17-9508-d49f366e0e77/image.png" alt=""></p>
<p align=center>의존성을 부여한 모습</P>

<p>위 코드는  Controller가 존재하기 위해서 MemberService가 있어야 하기에, 의존성을 직접적으로 부여한코드이다.</p>
<h1 id="3-ioc제어역전-inversion-of-control">3. IoC(제어역전 ,Inversion of Control)</h1>
<ul>
<li>개발자가 프레임워크의 기능을 호출하는 형태가 아니라 프레임워크가 개발자의 코드를 호출하기 때문에, 개발자는 전체를 직접 구현하지 않고 자신의 코드를 부분적으로 &quot;끼워넣기&quot; 하는 형태로 구현할 수 있다. 이는 개발자로 하여금 구현하고자 하는 특정 분야의 기능에 집중할 수 있도록 한다.</li>
</ul>
<ul>
<li><p>프레임워크가 객체의 생성, 소멸과 같은 라이프 사이클을 관리하며 스프링으로부터 필요한 객체를 얻어올 수도 있다.</p>
</li>
<li><p>객체의 의존성을 역전시켜 객체 간에 결합도를 줄이고 유연한 코드를 작성할 수 있게 하여 가독성 및 코드 중복, 유지보수를 편하게 할 수 있게 한다.</p>
</li>
</ul>
<h1 id="4-pojo">4. POJO</h1>
<ul>
<li><p>Plain Old Java Object, 단순한 자바 오브젝트</p>
</li>
<li><p>Pojo란 객체 지향적인 원리에 충실하면서 환경과 기술에 종속되지 않고 필요한 원리에 충실하면서 환경과 기술에 종속되지 않고 필요에 따라 재활용될 수 있는 방식으로 설계된 오브젝트를 말한다.</p>
<ul>
<li>이러한 POJO에 어플리케이션의 핵심 로직과 기능을 담아 설계하고 개발하는 방법을 POJO프로그래밍 이라고 할 수 있다.</li>
</ul>
</li>
</ul>
<blockquote>
<p>설명이 매우 간단해 보인다면 이 링크를 클릭하여더 자세한걸 알아보자 <a href="https://happyer16.tistory.com/entry/POJOplain-old-java-object%EB%9E%80">https://happyer16.tistory.com/entry/POJOplain-old-java-object%EB%9E%80</a></p>
</blockquote>
<h1 id="5-component-scan">5. Component scan</h1>
<ul>
<li>Component scan의 대상은 Spring이 scan을 하는 대상이 된다.</li>
</ul>
<h1 id="6-didependency-injection의-3가지-방법">6. DI(Dependency Injection)의 3가지 방법</h1>
<ul>
<li>Field Injection(필드 주입)</li>
<li>Setter Injection(수정자 주입)</li>
<li>Constructor Injection(생성자 주입), 현재는 이것을 많이 쓰고있음</li>
</ul>
<h2 id="여담-spring-boot에-db연결">여담 Spring Boot에 DB연결</h2>
<p>DB 연결 -&gt; DB생성 -&gt; gradle 추가,refresh -&gt; application수정 -&gt; class생성</p>
<p>DB연결, 생성까지는 여태 배워온것을 이용하여 cmd프롬포트창과 Dbeaver를 이용하여 만들어 준다.</p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/e2594bc7-1f6c-4c53-a1a6-11c2e83d17e0/image.png" alt=""></p>
<p>gradle은 다음과 같은 명령문을 넣어주자.</p>
<pre><code>    implementation &#39;org.springframework.boot:spring-boot-starter-jdbc&#39;
    implementation group: &#39;com.oracle.database.jdbc&#39;, name: &#39;ojdbc6&#39;, version: &#39;11.2.0.4&#39;</code></pre><p> 해당 명령문은 <a href="https://mvnrepository.com/artifact/com.oracle.jdbc/ojdbc8/12.2.0.1">https://mvnrepository.com/artifact/com.oracle.jdbc/ojdbc8/12.2.0.1</a> 에서도 확인이 가능하다.</p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/9892e8c2-b1f9-44ac-b654-e5db3d2b5893/image.png" alt=""></p>
<p>application에 위 사진처럼 설정을 해주어야 한다. username, password의 경우 자신이 직접 설정한 DB ID와 PW를 입력해주면 된다.</p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/fbfefb75-9af2-4b1b-93bb-1d15a8514e0a/image.png" alt=""></p>
<p>DTO를 만들어주고,</p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/e0774ede-786b-4145-9201-959599a5157c/image.png" alt=""></p>
<p>Repository를 만든 뒤</p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/51ae39db-32b8-429f-84de-0538ba303e9a/image.png" alt=""></p>
<p>service java파일을 만들어준다.</p>
<blockquote>
<p>@Transactional은 차후에 나와야 할 코드이지만 지금 코드를 재활용하느라 적혀있다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/01adb0be-d970-4aa4-a22c-ec0b4b5fe927/image.png" alt=""></p>
<p>Repository를 상속받아오고, 의존성을 주입해준 모습이다.</p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/ccd331b9-baad-4417-93bc-bd5b8db32a0b/image.png" alt=""></p>
<p>그리고 그 안에 상속받아온것을 바탕으로 명령코드를 짜주면 완성이다.
<del>Controller는 어디다 팔아먹었냐 하시는분이 있을것같다</del></p>
<h1 id="7-jpa">7. JPA</h1>
<ul>
<li>JPA는 기존의 반복 코드는 물론이고, 기본적인 SQL도 JPA가 직접 만들어서 실행해준다.</li>
<li>JPA를 사용하면, SQL과 데이터 중심의 설계에서 객체 중심의 설계로 패러다임을 전환할 수 있다.</li>
<li>JPA를 사용하면 개발 생산성을 크게 높일 수 있다.</li>
</ul>
<blockquote>
<p>gardle부분에 다음과 같은 문장을 넣어 Refresh를 해주자.
implementation &#39;org.springframework.boot:spring-boot-starter-data-jpa&#39;</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/79c69fa8-60ba-45b9-9f1d-991479cc3ab9/image.png" alt=""></p>
 <center>적용한 모습</center>


<p><img src="https://velog.velcdn.com/images/kyj-8754/post/fa13b77e-ace2-43a2-ab44-d6055927f944/image.png" alt=""></p>
<p>application역시 위와 같이 명령문을 넣어주면 JPA를 사용할 준비가 전부 되었다.</p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/4d4aa09e-a5e1-49b6-82d4-79c2ec31e1cc/image.png" alt=""></p>
<p><strong>무려 JPA를 사용하면 위에 그 긴 코드가 이런식으로 짧아진다.</strong></p>
<blockquote>
<p>잘 사용한다면 좋은 코드이지만, 아직 공부가 부족한 터라 이부분은 더 해봐야 할 것 같다.</p>
</blockquote>
<h1 id="8solid">8.SOLID</h1>
<ul>
<li>클린코드로 유명한 로버트 마틴이 좋은 객체지향 설계의 5가지 원칙을 정리</li>
</ul>
<blockquote>
<p>Spring관련 기술면접때 주요한 부분이므로 기억해두자.</p>
</blockquote>
<h3 id="1-srp--단일-책임-원칙single-responsibility-principle">1. SRP : 단일 책임 원칙(Single Responsibility Principle)</h3>
<pre><code>- 한 클래스는 하나의 책임만 가져야 한다.
- 하나의 책임이라는 것은 모호하다
- 그 책임이라는 것은 클 수도 있고, 작을 수도 있다.
- 책임은 문맥과 상황에 따라 다르다.
- 중요한 기준은 변경이다. 변경이 있을 때 파급효과가 적으면 단일 책임원칙을 잘 따르는 것이다.</code></pre><h3 id="2-ocp--개방-폐쇄-원칙openclosed-principle">2. OCP : 개방-폐쇄 원칙(Open/Closed Principle)</h3>
<pre><code>- 확장에는 열려있고, 수정, 변경에는 닫혀있다.</code></pre><h3 id="3-lsp--리스코프-치환-원칙liskov-substitution-principle">3. LSP : 리스코프 치환 원칙(Liskov Substitution Principle)</h3>
<pre><code>- 프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다.
- 다형성에서 하위 클래스는 인터페이스 규약을 다 지켜야 한다는 것, 다형성을 지원하기 위한 원칙, 인터페이스를 구현한 구현체를 믿고 사용하려면, 이 원칙이 필요하다.
- 단순히 컴파일에 성공하는 것을 넘어서는 이야기</code></pre><h3 id="4-isp--인터페이스-분리-원칙interface-segregation-principle">4. ISP : 인터페이스 분리 원칙(Interface Segregation Principle)</h3>
<pre><code>- 특정 클라이언트를 위한 인터페이스를 여러 개가 범용 인터페이스 하나보다 낫다.
- 자동차 인터페이스와 사용자 클라이언트 인터페이스를 분리하면, 서로 수정이 되더라도 영향을 주지 않는다.
- 인터페이스가 명확해지고, 대체 가능성이 높아진다.</code></pre><h3 id="5-dip--의존관계-역전-원칙dependency-inversion-principle">5. DIP : 의존관계 역전 원칙(Dependency Inversion Principle)</h3>
<pre><code>- 프로그래머는 &quot;추상화에 의존해야지, 구체화에 의존하면  안된다.&quot;
- 쉽게 이야기해서 구현 클래스에 의존하지 말고 인터페이스에 의존하라는 뜻
- 구현체에 의존하면 변경이 아주 어려워 진다.</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[2차 팀별 프로젝트 후기]]></title>
            <link>https://velog.io/@kyj-8754/2%EC%B0%A8-%ED%8C%80%EB%B3%84-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@kyj-8754/2%EC%B0%A8-%ED%8C%80%EB%B3%84-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Wed, 20 Sep 2023 08:24:45 GMT</pubDate>
            <description><![CDATA[<p>2023.8.30~2023.9.19 약 2주하고 조금 더 있던 시간동안 국비프로젝트로 한것에 대한 후기이다.</p>
<blockquote>
<p>당시 6명으로 이루어진 팀으로 시작한 프로젝트로  당시 배운 jsp를 토대로 웹 사이트를 구현하는것을 깃허브에 업로드하면서 결과물을 도출하는것이 목표였다.<br/>
우리가 구현하기로 한것은 회원 DB를 통해 음식을 주문하면 DB로 넘기는, 흔히볼 수 있는 음식 프렌차이즈의 웹사이트를 만들어보는것이였다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/bcd9adbb-d5d2-4a65-8641-cdf1d76dd2e0/image.png" alt=""></p>
<p><em><del>그때는 몰랐지 그 짧은 시간에 할수없다고</del></em></p>
<p>프로젝트 시작에 앞서 강사분은 필수로 구현해야하는 목록들을 나열해 주었고, 우리 나름대로 역할을 분담하며 FE, BE에 인원을 분배했다.</p>
<p>그런데 내가 1주일동안 건강 트러블이 생겨 학원을 나오지못하고, 스케쥴에 지장이 생겼다. 심지어 학원을 못나오는 동안 집에서라도 도움되고자 했으나 설정문제가 생기면서 그걸 해결하고자 온몸을 비틀었다...</p>
<p><a href="https://velog.io/@kyj-8754/Git-Eclipse-Pull-Error-%EC%B4%88%EC%8B%AC%EC%9E%90-%EC%A3%BC%EC%9D%98">해결방법</a> 이 에러를 해결 못했더라면 그 이후 프로젝트에도 심각한 트러블이 생겼을것이다....</p>
<p>이후 어찌저찌 건강이 돌아와 프로젝트 스케쥴이 일주일밖에 남지않았고... 나는 음식 주문하는 창을 만들어야 했다.</p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/3a8e2737-e636-4950-b89d-da4e7c506bcc/image.png" alt=""></p>
<p>도미노피자, 피자 반올림샵 등 여러 사이트를 전전긍긍하면서 UI, UX 개발자 코드소스를 참고하여 구현했다.</p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/0b69da8a-1739-4b3c-a477-680e79d8be61/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/09fdd8be-9d01-41db-9c0d-5f435f0424a5/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/e22654e3-4776-4fce-84a4-52eeb5fcbb22/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/34422a0f-6185-4b55-a318-3ece92051a3f/image.png" alt=""></p>
<blockquote>
<p>이걸 하면서 깨달은게있다.. 난 죽었다 깨어나도 디자인은 하면 안돼겠구나...</p>
</blockquote>
<p>100% 구현은 모두 못한것 같다. 사실 타 사이트처럼 이쁘게 하고 싶었지만 다른 사람들과의 협업 (DB로 넘기는 작업을 위해 날려야하는 부분이 어느정도 있었다.)</p>
<p>일주일이라는 스케쥴 속 구현해야하는 리스트들이 넘쳤기 때문이다.</p>
<p>원래 프로젝트라는게 술술 진행되는게 아니란건 잘 알지만 코드를 짜다보니 FE, BE 모두 만지는 상황이 되어 정신이 없었다.</p>
<p>타 사이트에서 개발자 코드를 리딩하면서 얼마나 보기쉽게 하기위해 노력하였는지 세삼스레 감탄만 하면서 내가 짠 코드는 왜 여기서 구현이 안돼지 하고 머리 싸메면서 19일 발표 당일까지 코드를 짜느라 정신없었다..</p>
<p>일기장은 그만 쓰고 다음 포스팅에서는 내가 짠 코드를 리딩하는글을 올릴까 한다.
그게 길다보니 언제될지는 미지수다..</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Boot 초기 세팅]]></title>
            <link>https://velog.io/@kyj-8754/Spring-Boot-%EC%B4%88%EA%B8%B0-%EC%84%B8%ED%8C%85</link>
            <guid>https://velog.io/@kyj-8754/Spring-Boot-%EC%B4%88%EA%B8%B0-%EC%84%B8%ED%8C%85</guid>
            <pubDate>Wed, 20 Sep 2023 07:49:55 GMT</pubDate>
            <description><![CDATA[<h1 id="1-framework란">1. Framework란?</h1>
<ul>
<li>뼈대나 근간을 이루는 코드들의 묶음</li>
<li>프로그램의 기본 흐름이나 구조를 정하고, 이 구조에 자신의 코드를 추가하는 방식으로 개발할 수 있도록 하는 프로그래밍의 기본틀을 의미</li>
<li>개발에 필요한 구조가 제공되고, 여기에 필요한 부분을 조립하는 형태로 개발이 진행된다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/b20c469d-16ef-4a53-ab6b-3af2118d5c39/image.png" alt=""></p>
<h1 id="2-라이브러리란">2. 라이브러리란?</h1>
<ul>
<li>라이브러리란 자주 사용되는 조직을 재사용하기 편리하도록 잘 정리한 일련의 코드들의 집합</li>
</ul>
<h1 id="3-framework-vs-라이브러리">3. Framework vs 라이브러리</h1>
<ul>
<li>프레임워크는 자동차의 프레임, 즉 기본적으로 구성하고있는 뼈대를 말한다.</li>
<li>라이브러리는 자동차의 기능을 하는 부품을 의미한다.</li>
<li>한 번 정해진 자동차의 프레임은 바꿀 수 없다. 즉 소형차를 만들기 위해 뼈대(Framework)를 사용했다면, 이 뼈대로 다른 종류의 차체를 만드는게 불가능하다</li>
<li>그러나 기타 부속품(라이브러리)은 다른 종류로 쉽게 변경이 가능하다.</li>
</ul>
<h1 id="4-spring이란">4. Spring이란?</h1>
<ul>
<li>스프링 프레임워크는 자바 플랫폼을 위한 오픈소스 애플리케이션 프레임워크로서 간단히 스프링이라고 도 불린다.</li>
<li>동적인 사이트를 개발하기 위한 여러 가지 서비스를 제공하고있다.</li>
</ul>
<h1 id="5-spring-vs-spring-boot">5. Spring vs Spring boot</h1>
<p>기존 Spring에서는 설정하기 위해 많은 코드를 작성해야 했지만  boot로 넘어오면서 이런것들이 간략해지고 자동으로 잡아주는 등 차이가 많다. 그러나 여기에서는 링크로 설명을 대처한다.</p>
<p><a href="https://programforlife.tistory.com/68">https://programforlife.tistory.com/68</a></p>
<h1 id="6-spring-boot-설정">6. Spring Boot 설정</h1>
<p><a href="https://spring.io/">https://spring.io/</a></p>
<p>위 설치링크로들어가서 Spring boot를 설치한다.</p>
<h2 id="6-1-embed-tomcatembedded-tomcat">6-1. Embed Tomcat(Embedded Tomcat)</h2>
<ul>
<li>스프링부트는 내장형 톰캣을 가지고 있기 때문에 별도의 톰캣을 설정할 필요가 없어졌으며, 그렇기 때문에 독립적으로 실행 가능한 jar로 손쉽게 배포가 가능해졌다..</li>
</ul>
<ul>
<li>대신 서버 port는 여전히 설정을 해줘야 한다. </li>
</ul>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/61a8f932-54bc-4513-8ac3-4445bacc4554/image.png" alt=""></p>
<blockquote>
<p>server.port = 9090 으로 port값을 설정해주는 장면</p>
</blockquote>
<h2 id="6-2-autoconfigurator">6-2 AutoConfigurator</h2>
<ul>
<li>공통적으로 필요한 DispatcherServlet과 같은 설정을 어노테이션을 이용하여 대신할 수 있도록 해줍니다.</li>
<li>스프링 부트의 main메서드는 @SptingBootApplication 어노테이션을 가지고 있는데 이것은 ComponentScan + configuration + EnableAutoConfiguration을 합친 어노테이션 이라고 볼 수 있다.</li>
</ul>
<h1 id="7-환경설정">7. 환경설정</h1>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/ba001208-ff2b-4b1a-ba19-5f7df68adefa/image.png" alt=""></p>
<blockquote>
<p>STStool 설치</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/d74ab512-99e9-402f-8268-429244bd587a/image.png" alt=""></p>
<p>Preference -&gt; &#39;enc&#39;검색 -&gt; content types Default encoding -&gt; utf-8 입력후 update, 적용 , Content Types에 text부분도 동일하게 UTF-8세팅을 해준다.</p>
<blockquote>
<p>이클립스 하던것처럼 동일하게 세팅한다고 보면된다.</p>
</blockquote>
<h1 id="8-프로젝트-세팅">8. 프로젝트 세팅</h1>
<ul>
<li><a href="https://start.spring.io/">https://start.spring.io/</a>
gradle을 사용하기 위하여 위 링크를 들어가 설정을 맞춰준다.</li>
</ul>
<h2 id="8-1-project--사용할-빌드-툴-선택maven--gradle">8-1 Project : 사용할 빌드 툴 선택(Maven / Gradle)</h2>
<ul>
<li>Maven, Gradle은 프로젝트에 필요한 의존성을 관리하고 빌드의 라이프 사이클을 관리해주는 틀이다.
과거에는 Maven을, 최근에는 Gradle을 사용하는 추세이다.</li>
</ul>
<h2 id="8-2-spring-boot--버전선택">8-2. Spring Boot : 버전선택</h2>
<ul>
<li>SNAPSHOT이 붙은 것은 현재 개발중인 버전, M(Minor)은 정식 릴리즈 되지 않은 버전이고 아무것도 붙어있지
않은것이 정식 릴리즈된 버전이다.</li>
</ul>
<p>** LTS</p>
<ul>
<li>Long Term Support</li>
<li>오랜 기간 지원될 버전</li>
</ul>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/57841b23-561e-4e27-bfaa-6df0b5bd15e0/image.png" alt=""></p>
<p>현재 사용한 Gradle은 위와 같은 세팅으로 갈것이다.</p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/f5ec5aeb-7de2-480b-b51a-6b5d8f3ee193/image.png" alt=""></p>
<blockquote>
<p>설정한 Gradle 파일을 import한 모습</p>
</blockquote>
<ul>
<li><p>프로젝트 우클릭, New로 들어가서 html파일을 만드려 하면 목록이 나오지 않는다. 따라서 아래 방법을 하여 설정을 잡아준다.</p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/4f25874f-defe-4859-a896-7a8560a53d2a/image.png" alt=""></p>
</li>
</ul>
<p>marketplace -&gt; &#39;java and web&#39; 맨 위나오는거 설치</p>
<h1 id="9-dispatcherservlet">9. DispatcherServlet</h1>
<blockquote>
<p>강사님은 이 파트가 중요하다 강조했다.</p>
</blockquote>
<ul>
<li>스프링 MVC도 프론트 컨트롤러 패턴으로 구현되어 있다.</li>
<li>스프링 MVC의 프론트 컨트롤러가 바로 디스패처 서블릿이다.</li>
<li>그리고 이 디스패처 서블릿이 바로 스프링 MVC의 핵심이다.</li>
<li>DispatcherServlet도 부모 클래스에서 HttpServlet을 상속 받아서 사용하고,
서블릿으로 동작한다.</li>
<li>DispatcherServelt -&gt; FrameworkServlet -&gt; HttpServletBean -&gt; HttpServlet</li>
</ul>
<h1 id="10-프레임워크-흐름">10. 프레임워크 흐름</h1>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/f6b4c2b8-a25a-485b-bb51-6221bfec9485/image.png" alt=""></p>
<ul>
<li>핸들러 조회 : 핸들러 매핑을 통해 요청 URL에 매핑된 핸들러(컨트롤러)를 조회한다.</li>
<li>핸들러 어댑터 조회 : 핸들러를 실행할 수 있는 핸들러 어댑터를 조회한다.</li>
<li>핸들러 어댑터 실행 : 핸들러 어댑터를 실행한다. </li>
<li>viewResolver 호출 : 뷰리졸버를 찾고 실행한다.</li>
<li>view 반환 : 뷰 리졸버는 뷰의 논리이름을 물리이름으로 바꾸고, 렌더링 역할을 담당하는 뷰 객체를 반환한다.
 -&gt; forward, redirect
 -&gt; 기본방식 : forward방식이다.</li>
</ul>
<h1 id="11-model이란">11. Model이란?</h1>
<ul>
<li>Contoller에서 데이터를 Model에 담는다. view는 Model에 담겨있는 데이터만 골라서 화면에 바인딩 해준다. </li>
<li>Model객체는 컨트롤러에서 데이터를 생성해 이를 jsp, HTML 즉, View단에 전달하는 역할을 한다.</li>
<li>HashMap 형태를 가지고 있고, 키(key)와 벨류(Value)값을 저장한다.{request.setAttribute() 와 비슷한 역할}</li>
</ul>
<h1 id="12-modelandview">12. ModelAndView</h1>
<ul>
<li>Model에서 View영역이 좀 더 확장</li>
<li>Model과 View를 동시에 설정이 가능한 컨트롤러는 Model과 View가 모두 리턴 가능</li>
</ul>
<h1 id="13-model-vs-modelandview">13. Model VS ModelAndView</h1>
<ul>
<li>데이터만 저장한다. VS 데이터와 이동하고자 하는 Viewpage를 같이 저장한다.</li>
</ul>
<h1 id="14-계층-구조">14. 계층 구조</h1>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/e21d0c5c-e7ef-4739-9614-e90e0198d3c7/image.png" alt=""></p>
<ul>
<li>컨트롤러 : 웹 mvc의 컨트롤러 역할</li>
<li>서비스 : 핵심 비즈니스 로직 구현</li>
<li>레파지토리 : 데이터베이스에 접근</li>
<li>도메인 : 비즈니스 도메인 객체, 예)회원, 주문, 쿠폰 등등</li>
</ul>
<h1 id="코드-응용">코드 응용</h1>
<p> <img src="https://velog.velcdn.com/images/kyj-8754/post/918e7274-c7c8-4ad7-a142-3ec2b8c1c872/image.png" alt=""></p>
<p>위와 같은 파일로 이루어져있으며, local server를 가동할때, 기본 index는 static에서 serch하기 때문에 index.html이 resource에 static에 있는것을 볼 수 있다.</p>
<pre><code class="language-spring">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    Hello
    &lt;a href=&quot;/hello&quot;&gt;hello&lt;/a&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p> 코드는 jsp와 매우 흡사하다.</p>
<pre><code class="language-java"> package com.codingbox.core.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

/*
 * @Controller : Controller 역할을 하는 Class에 붙여준다.
 *                 Controller에서 mapping url을 찾는다.
 *                 현재 : localhost:9090/hello
 */
@Controller
public class HelloController{


    /*
     * &#39;/hello&#39; 값 mapping시켜줌 
     * @GerMapping : get방식의 요청
     */

    @GetMapping(&quot;/hello&quot;)
    public String hello(Model model) {
        System.out.println(&quot;hello 도착&quot;);
        model.addAttribute(&quot;data&quot;, &quot;Hello!!&quot;);
        return &quot;hello&quot;; // hello.html 화면을찾아서 return
        /*
         * 컨트롤러에서 리턴값으로 문자를 반환하면
         * 뷰 리졸버(viewResolver)가 화면을 찾아서 처리한다.
         *     - 스프링 부트 템플릿엔진 기본 viewName 매핑
         *  - resources:templates/+{viewName}+.html
         */
    }</code></pre>
<p> 위 코드는 Controller를 코드한 모습이다. 여기서 특이점이라면 어노테이션 부분이라고 볼 수있다. Controller라고 지정해주면 프레임워크에서 자동으로 잡아줘 jsp보다는  쉽게 코드가 구현되는 모습을 보여준다.</p>
<pre><code> &lt;!DOCTYPE html&gt;
&lt;html xmlns:th=&quot;http://www.thymeleaf.org&quot;&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;p th:text=&quot;&#39;안녕하세요. &#39;+${data}&quot;&gt; 안녕하세요. 손님&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><p> <img src="https://velog.velcdn.com/images/kyj-8754/post/96d55807-2583-4d51-bd0c-ebc6fba78c15/image.png" alt=""></p>
<blockquote>
<p>구현한 모습</p>
</blockquote>
<p> 만일 data에 값이 없다면 p태그에 작성된 <code>안녕하세요. 손님</code> 이 출력된다. jsp에서는 data가 null이라면 오류가 나지만 부트는 그렇지 않다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Git Eclipse Pull Error (초심자 주의)]]></title>
            <link>https://velog.io/@kyj-8754/Git-Eclipse-Pull-Error-%EC%B4%88%EC%8B%AC%EC%9E%90-%EC%A3%BC%EC%9D%98</link>
            <guid>https://velog.io/@kyj-8754/Git-Eclipse-Pull-Error-%EC%B4%88%EC%8B%AC%EC%9E%90-%EC%A3%BC%EC%9D%98</guid>
            <pubDate>Fri, 15 Sep 2023 01:22:59 GMT</pubDate>
            <description><![CDATA[<p>팀프로젝트 진행 과정 중 IntelliJ로 만든 Dynamic Web Project를 깃허브로 올리고, 그것을 Eclipse로 Pull해오는 과정에서, 파일에 Libraries가 없는 경우가 발생하여 이를 해결하는 과정을 기록해본다.</p>
<p>당시, 자동완성 기능 사용시, Computing Proposals 0%... 라면서 기타 오류도 발생 하였다.</p>
<p>이는 Build Path를 잡아줘야 해결이 가능한 상황이였으나, 프로젝트를 마우스 우클릭해도 보이지 않는 상황이였다.</p>
<p>해결방안은 Configure에 들어있었다.</p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/8caab42f-77b5-41b8-925a-054db4bff517/image.png" alt=""></p>
<p>임시로 깃에 있는 프로젝트를 가져오기위해 import한다.</p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/633f6c90-f08c-4686-958d-0ab92e265f09/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/52fada54-4b42-4bea-84da-4315deb0feac/image.png" alt=""></p>
<p>가져왔으나, 라이브러리가 없다 프로젝트를 우클릭해보지만 build path는 당연히 없다.</p>
<p>여기서 대충 2~3일동안 구글링하면서, 헤맸다.</p>
<blockquote>
<p>원인은 저 pom.xml에 있었다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/cd8bf834-bdc7-4589-89d3-cdb2cff05a97/image.png" alt=""></p>
<p>정답은 Configure 에서 Maven Project로 변환하는 방법이였다.</p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/5d8210ac-d63a-4456-aa81-2b29681fe211/image.png" alt=""></p>
<blockquote>
<p>변환 이후 제대로 잡히는 모습</p>
</blockquote>
<p>이후에 프로젝트를 우클릭해서 보면 Build Path도 잡히고, jar도 수정해서 잘되겠구나! 하지만</p>
<p>javax.servlet.jsp.JspException cannot be resolved to a type</p>
<p>이번엔 이 에러가 눈에 잡힌다. 당시 server를 tomcat으로 만들어서 연결했는데 뭐지해서 여기서 또 몇 일 고생했다.. 프로젝트가 server runtime이 제대로 안잡혀있어 생기는 문제였다.</p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/7a741951-61b8-4f19-982d-55ae1e6d04f7/image.png" alt="">
프로젝트 우클릭 -&gt; Properties의 -&gt; Build Pathd -&gt; Add Library 클릭</p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/66b20881-2502-492a-9fb3-40c11c6ca929/image.png" alt=""></p>
<p>Server Runtime에서 만든 서버 Apache Tomcat를 설정하고 Finish를 클릭하면 
javax.servlet.jsp.JspException cannot be resolved to a type 에러는 사라지고 이제 클린하게 프로젝트를 수정해보자.</p>
<h2>후기</h2>
정말.. 팀프로젝트를 처음 하면서 이러한 에러가 집에서 뜨니 환장할 노릇이였다..
구글링을 해봐도 build path를 누르면 된다는데 애초에 그 탭이 없어서 시작도 못하고 바보가 되는 기분이였다. <br/> 불행중 다행으로 구글링을 하던 어느 날 뜻하지않게 방법을 찾아 위와 같은 방법을 알게 되었고, 이는 앞으로 나의 훌륭한 밑거름이 될거라 생각이 들며, 나와 같은 초보적인(?) 이슈를 겪는 사람이 있을까봐 몇 자 적어보았다.

]]></description>
        </item>
        <item>
            <title><![CDATA[HTML 4일차]]></title>
            <link>https://velog.io/@kyj-8754/HTML-4%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@kyj-8754/HTML-4%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Wed, 16 Aug 2023 07:06:25 GMT</pubDate>
            <description><![CDATA[<h1 id="1-파일-찾아보기">1. 파일 찾아보기</h1>
<h2 id="1-1-파일-찾아보기">1-1. 파일 찾아보기</h2>
<ul>
<li>type=&quot;file&quot;요소는 웹 프로그램으로 파일을 전송할 수 있도록 찾아보기 버튼을 표시하며, 한 번에 하나의 파일만 첨부 가능하다</li>
<li>단, 이 요소가 사용될 경우에는 반드시 <code>&lt;form&gt;</code>태그에 enctype=&quot;multipart/form-data&quot; 속성이 명시 되어야한다. </li>
<li>브라우저의 보안을 위해 이 요소에서는 value속성값을 지정할 수 없다.</li>
</ul>
<h1 id="2-프로그램-전송">2. 프로그램 전송</h1>
<h2 id="2-1-method-속성은-전송방식을-의미한다">2-1. method 속성은 전송방식을 의미한다.</h2>
<ul>
<li><p>get : 입력된 모든 내용이 URL에 포함되어 전송된다.(기본값)</p>
<ul>
<li>post : 입력된 모든 내용이 URL에 노출되지 않고 전송된다.<blockquote>
<p>회원가입 등 민감한 개인정보를 서버로 넘길때 속성값을 노출하지 않기 위해 post를 사용한다.</p>
</blockquote>
<h1 id="3-sementic-tag">3. sementic tag</h1>
</li>
</ul>
</li>
<li><p>의미론적 tag
<img src="https://velog.velcdn.com/images/kyj-8754/post/1d5fa74c-5174-424d-b5c8-12fe92cb320e/image.png" alt=""></p>
<blockquote>
<p>div태그를 이용하여 위와같이 분류를 가능하지만, 타 사이트 검색 포털사이트에 노출이 적다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/79080f17-2d67-48d7-b50e-b4f221764dfa/image.png" alt=""></p>
</li>
</ul>
<blockquote>
<p>위와 같이 세세한 분류를 통해 노출을 높인다.</p>
</blockquote>
<ul>
<li><p>문서의 정보를 보다 잘 표현하기 위해서는 의미에 맞는 태그를 잘 사용해야 한다. 특히 HTML5에서는 웹페이지에서 통상 많이 사용하는 구조에 의미를 분명히 부여하기 위해서 의미론적 태그(semantic element)를 새롭게 정의해서 제공하고있다.</p>
</li>
<li><p>SEO(Search Engine Optimiztion)
 -&gt; 검색을 최적화하기 위해서 우리가 제목, 부제목, 시멘틱 태그를 잘 활용한다면, 특정 키워드로 검색 했을 때, 내가 만든 웹사이트가 검색창에 노출될 수 있다.
 -&gt; 다시 말해서 검색 엔진이 내가 만든 웹사이트를 어떤 결과에 띄워줘야 될 지를 알려줄 수 있다.</p>
</li>
<li><p>유지보수성
 -&gt; 단순히 div tag로만 모든 구조를 짜는 것보다 더 한눈에 알아볼 수 있기 때문에, 다른 개발자들이 코드를 유지보수 하기가 더 편해진다.</p>
<pre><code class="language-javascript"> &lt;!DOCTYPE html&gt;
 &lt;html lang=&quot;en&quot;&gt;
 &lt;head&gt;
     &lt;meta charset=&quot;UTF-8&quot;&gt;
     &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
     &lt;title&gt;Document&lt;/title&gt;
 &lt;/head&gt;
 &lt;body&gt;
 &lt;header&gt;
     &lt;h1&gt;HTML&lt;/h1&gt;
 &lt;/header&gt;
 &lt;nav&gt;
     &lt;ol&gt;
         &lt;li&gt;기술소개&lt;/li&gt;
         &lt;li&gt;기본문법&lt;/li&gt;
         &lt;li&gt;하이퍼텍스트와 속성&lt;/li&gt;
         &lt;li&gt;리스트와 태그의 중첩&lt;/li&gt;
     &lt;/ol&gt;
 &lt;/nav&gt;

 &lt;section&gt;
     &lt;article&gt;
         &lt;h2&gt;선행학습&lt;/h2&gt;
             HTML 태그 사용 법
     &lt;/article&gt;
     &lt;article&gt;
         &lt;h2&gt;선행학습&lt;/h2&gt;
             HTML 태그 사용 법
     &lt;/article&gt;
 &lt;/section&gt;

 &lt;footer&gt;
     &lt;ul&gt;
         &lt;li&gt;&lt;a href=&quot;private.html&quot;&gt;개인정보 정책&lt;/a&gt;&lt;/li&gt;
         &lt;li&gt;&lt;a href=&quot;about.html&quot;&gt;회사소개&lt;/a&gt;&lt;/li&gt;
     &lt;/ul&gt;
 &lt;/footer&gt;
 &lt;/body&gt;</code></pre>
</li>
</ul>
<blockquote>
<p>위 태처럼 div 태그대신 시멘틱 태그를 이용한다면 유지&amp;보수 혹은 검색노출에 더 좋다 </p>
</blockquote>
<h1 id="4-css">4. CSS</h1>
<h2 id="4-1-css란">4-1. CSS란</h2>
<ul>
<li>Cascading Style Sheet</li>
<li>HTML태그에 옷(=디자인)을 입혀주는 기법</li>
<li>문서 전체의 일관성을 유지할 수 있고, 세세한 스타일 지정의 필요를 줄여준다.<blockquote>
<p>head 안에 값을 설정하여 사용</p>
</blockquote>
<h1 id="5-css-셀렉터">5. CSS 셀렉터</h1>
<h2 id="5-1-기본-셀렉터-종류">5-1. 기본 셀렉터 종류</h2>
</li>
<li>CSS에서 셀렉터를 작성하는 방법은 기본적으로 HTML의 태그이름, 클래스 속성, id속성에 대한 명시가 있다.</li>
</ul>
<h2 id="5-2-태그이름">5-2. 태그이름</h2>
<ul>
<li>selector{... css 본문 ...}</li>
<li>특정 태그를 가리킨다. HTML내에 동일 태그가 존재할 경우 모든 태그 요소를 일괄 처리 한다.</li>
</ul>
<h2 id="5-3-클래스">5-3. 클래스</h2>
<ul>
<li>.selector{... css 본문...}</li>
<li>셀렉터 이름 앞에 점(.)을 붙여 표시하고 HTML 태그의 class속성에 점을 제외한 이름을 명시하여 지정한다.</li>
<li>태그의 종류를 가지지 않고 여러 요소에 복수 지정이 가능하다.
(재사용의 목적)</li>
<li>이 경우 selector의 이름은 자유롭게 지정한다.</li>
</ul>
<h2 id="5-4-id">5-4. id</h2>
<ul>
<li>#selector{...css본문...}</li>
<li>셀렉터 이름앞에 #을 붙여 표시하고, HTML태그의 id속성에 #을 제외한 이름을 명시하여 지정한다.</li>
<li>id속성의 값은 HTML페이지 내에서 중복사용될 수 없다
(고유특성 명시)</li>
<li>이 경우 selector의 이름은 자유롭게 지정한다.</li>
</ul>
<h2 id="5-5-조합형-셀렉터">5-5. 조합형 셀렉터</h2>
<ul>
<li>tag.class{.. css 값...}</li>
<li>tag#id{...css 값...}</li>
<li>id값이나 class값을 태그 이름과 함께 명시하여 적용하는 형태</li>
<li>셀렉터가 지정하는 대상을 자세하게 명시할 수 있다.</li>
<li>태그,클래스,아이디를 독립적으로 명시할 때보다 우선적으로 적용된다.</li>
</ul>
<h2 id="5-6-and의-의미를-갖는-콤마-사용하기">5-6. and의 의미를 갖는 콤마(,) 사용하기</h2>
<ul>
<li>selector1, selector2, selector3, ... {...css 적용...}</li>
<li>여러 개의 요소에 동일한 CSS내용을 적용하기 위해 셀렉터를 콤마(,)로 구분하여 일괄 처리 할 수 있다.</li>
<li>태그, class, id 등 모든 형태의 CSS셀렉터가 가능하다.</li>
</ul>
<pre><code class="language-javascript">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
    &lt;title&gt;Document&lt;/title&gt;
    &lt;style&gt;
        /* 태그 셀렉터 */
        h2{ color :powderblue; }
        /* 클래스 셀렉터 - HTML태그의 class속성과 연결된다.*/
        .myclass{ color: tomato;}
        /* 아이디 셀렉터 - HTML태그의 id속성과 연결된다.*/
        #myid{ color: blue;}
        /* 조합형 - 두 조건이 충족하는 경우만 적용*/
        h2.myclass{color: rebeccapurple;}
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;div&gt;
        &lt;h1&gt;CSS 셀렉터를 알아봅시다.&lt;/h1&gt;
        &lt;p&gt;
            셀렉터란? -css가 적용될 대상을 지정하는 방법
        &lt;/p&gt;

        &lt;h2&gt;태그 셀렉터&lt;/h2&gt;

        &lt;h2 class=&quot;myclass&quot;&gt;클래스 셀렉터&lt;/h2&gt;
        &lt;p class=&quot;myclass&quot;&gt;
            서로 다른 여러 개의 요소에 적용할 수 있다.
        &lt;/p&gt;

        &lt;H2 id=&quot;myid&quot;&gt;아이디 셀렉터&lt;/H2&gt;
        &lt;p class=&quot;myclass&quot;&gt;
            태그+아이디, 태그+클래스 형식으로 조합이 가능하다.
        &lt;/p&gt;
    &lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/22ff58ad-6cf5-41f3-b869-be55ea3ed045/image.png" alt=""></p>
<blockquote>
<p>배운 셀렉터를 이용하여 여러 태그를 선정한 모습, 실제로 코드를 이용하여 확인해 보자.</p>
</blockquote>
<h1 id="6-링크와-관련된-가상-클래스-선택자">6. 링크와 관련된 가상 클래스 선택자</h1>
<ul>
<li><p>HTML태그에서 특정 상황이 발생했을 경우에만 적용되는 CSS셀렉터</p>
</li>
<li><p>가상클래스는 CSS의 셀렉터 이름 뒤에 &quot;:상황&quot;의 형식으로 명시한다.</p>
<p><img src="https://velog.velcdn.com/images/kyj-8754/post/665c4607-9708-4a53-b906-2a784e59cdc8/image.png" alt=""></p>
</li>
</ul>
<h1 id="7-객체">7. 객체</h1>
<h2 id="7-1-객채의-생성-과정">7-1 객채의 생성 과정</h2>
<ul>
<li><p>빈 객체의 생성
 -&gt; 아무런 기능이 없는 상태의 빈 객체를 생성
 -&gt; prototype</p>
</li>
<li><p>변수의 추가
 -&gt; 빈 객체 안에 변수들을 추가 해 넣는다.
 -&gt; 용도에 따라서 객체를 생성하고, 변수를 그룹화하기 위해서 사용한다.</p>
</li>
<li><p>함수의 추가
 -&gt; 빈 객체 안에 함수들을 추가 해 넣는다.
 -&gt; 기능은 서로 다르지만, 용도가 비슷한 함수들을 하나의 그룹으로 묶기 위한 단위가 객체이다.</p>
</li>
</ul>
<h2 id="7-2-빈-객체의-생성">7-2. 빈 객체의 생성</h2>
<pre><code>let people = {}</code></pre><h2 id="7-3-변수의-추가">7-3. 변수의 추가</h2>
<ul>
<li><p>객체안에 추가되어 있는 변수를 멤버변수 혹은 프로퍼티 라고 한다.</p>
</li>
<li><p>변수를 추가하기 위해서는 객체이름.변수명 = 값의 형태를 사용한다.</p>
</li>
<li><p>선언을 위한 별도의 키워드는 사용되지 않는다.</p>
<p> people.name = &quot;자바학생&quot;;
 people.age = 20;</p>
</li>
</ul>
<h2 id="8-웹-브라우저와-javascipt">8. 웹 브라우저와 Javascipt</h2>
<ul>
<li>HTML : 정보</li>
<li>CSS : 디자인</li>
<li>JavaScript : 웹 브라우저 HTML 제어</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[HTML 3일차]]></title>
            <link>https://velog.io/@kyj-8754/HTML-3%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@kyj-8754/HTML-3%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Fri, 11 Aug 2023 07:19:18 GMT</pubDate>
            <description><![CDATA[<h1 id="1-파일-찾아보기">1. 파일 찾아보기</h1>
<h2 id="1-1-파일-찾아보기">1-1. 파일 찾아보기</h2>
<ul>
<li>type=&quot;file&quot;요소는 웹 프로그램으로 파일을 전송할 수 있도록 찾아보기 버튼을 표시하며, 한 번에 하나의 파일만 첨부 가능하다</li>
<li>단, 이 요소가 사용될 경우에는 반드시 <form>태그에 enctype=&quot;multipart/form-data&quot; 속성이 명시 되어야한다. </li>
<li>브라우저의 보안을 위해 이 요소에서는 value속성값을 지정할 수 없다.</li>
</ul>
<h1 id="2-프로그램-전송">2. 프로그램 전송</h1>
<h2 id="2-1-method-속성은-전송방식을-의미한다">2-1. method 속성은 전송방식을 의미한다.</h2>
<ul>
<li>get : 입력된 모든 내용이 URL에 포함되어 전송된다.(기본값)<ul>
<li>post : 입력된 모든 내용이 URP에 노출되지 않고 전송된다.</li>
</ul>
</li>
</ul>
<h1 id="3-sementic-tag">3. sementic tag</h1>
<ul>
<li><p>의미론적 tag</p>
</li>
<li><p>문서의 정보를 보다 잘 표현하기 위해서는 의미에 맞는 태그를 잘 사용해야 한다. 특히 HTML5에서는 웹페이지에서 통상 많이 사용하는 구조에 의미를 분명히 부여하기 위해서 의미론적 태그(semantic element)를 새롭게 정의해서 제공하고있다.</p>
</li>
<li><p>SEO(Search Engine Optimiztion)
 -&gt; 검색을 최적화하기 위해서 우리가 제목, 부제목, 시멘틱 태그를 잘 활용한다면, 특정 키워드로 검색 했을 때, 내가 만든 웹사이트가 검색창에 노출될 수 있다.
 -&gt; 다시 말해서 검색 엔진이 내가 만든 웹사이트를 어떤 결과에 띄워줘야 될 지를 알려줄 수 있다.</p>
</li>
<li><p>유지보수성
 -&gt; 단순히 div tag로만 모든 구조를 짜는 것보다 더 한눈에 알아볼 수 있기 때문에, 다른 개발자들이 코드를 유지보수 하기가 더 편해진다.</p>
</li>
</ul>
<h1 id="4-css">4. CSS</h1>
<h2 id="4-1-css란">4-1. CSS란</h2>
<ul>
<li>Cascading Style Sheet</li>
<li>HTML태그에 옷(=디자인)을 입혀주는 기법</li>
<li>문서 전체의 일관성을 유지할 수 있고, 세세한 스타일 지저의 필요를 줄여준다.</li>
</ul>
<h1 id="5-css-셀렉터">5. CSS 셀렉터</h1>
<h2 id="5-1-기본-셀렉터-종류">5-1. 기본 셀렉터 종류</h2>
<ul>
<li>CSS에서 셀렉터를 작성하는 방법은 기본적으로 HTML의 태그이름, 클래스 속성, id속성에 대한 명시가 있다.</li>
</ul>
<h2 id="5-2-태그이름">5-2. 태그이름</h2>
<ul>
<li>selector{... css 본문 ...}</li>
<li>특정 태그를 가리킨다. HTML내에 동일 태그가 존재할 경우 모든 태그 요소를 일괄 처리 한다.</li>
</ul>
<h2 id="5-3-클래스">5-3. 클래스</h2>
<ul>
<li>.selector{... css 본문...}</li>
<li>셀렉터 이름 앞에 점(.)을 붙여 표시하고 HTML 태그의 class속성에 점을 제외한 이름을 명시하여 지정한다.</li>
<li>태그의 종류를 가지지 않고 여러 요소에 복수 지정이 가능하다.
(재사용의 목적)</li>
<li>이 경우 selector의 이름은 자유롭게 지정한다.</li>
</ul>
<h2 id="5-4-id">5-4. id</h2>
<ul>
<li>#selector{...css본문...}</li>
<li>셀렉터 이름앞에 #을 붙여 표시하고, HTML태그의 id속성에 #을 제외한 이름을 명시하여 지정한다.</li>
<li>id속성의 값은 HTML페이지 내에서 중복사용될 수 없다
(고유특성 명시)</li>
<li>이 경우 selector의 이름은 자유롭게 지정한다.</li>
</ul>
<h2 id="5-5-조합형-셀렉터">5-5. 조합형 셀렉터</h2>
<ul>
<li>tag.class{.. css 값...}</li>
<li>tag#id{...css 값...}</li>
<li>id값이나 class값을 태그 이름과 함께 명시하여 적용하는 형태</li>
<li>셀렉터가 지정하는 대상을 자세하게 명시할 수 있다.</li>
<li>태그,클래스,아이디를 독립적으로 명시할 때보다 우선적으로 적용된다.</li>
</ul>
<h2 id="5-6-and의-으미리르-갖는-콤마-사용하기">5-6. and의 으미리르 갖는 콤마(,) 사용하기</h2>
<ul>
<li>selector1, selector2, selector3, ... {...css 적용...}</li>
<li>여러 개의 요소에 동일한 CSS내용을 적용하기 위해 셀렉터를 콤마(,)로 구분하여 일괄 처리 할 수 있다.</li>
<li>태그, class, id 등 모든 형태의 CSS셀렉터가 가능하다.</li>
</ul>
<h1 id="6-링크와-관련된-가상-클래스-선택자">6. 링크와 관련된 가상 클래스 선택자</h1>
<ul>
<li>HTML태그에서 특정 상황이 발생했을 경우에만 적용되는 CSS셀렉터</li>
<li>가상클래스는 CSS의 셀렉터 이름 뒤에 &quot;:상황&quot;의 형식으로 명시한다.</li>
</ul>
<h1 id="7-객체">7. 객체</h1>
<h2 id="7-1-객채의-생성-과정">7-1 객채의 생성 과정</h2>
<ul>
<li><p>빈 객체의 생성
 -&gt; 아무런 기능이 없는 상태의 빈 객체를 생성
 -&gt; prototype</p>
</li>
<li><p>변수의 추가
 -&gt; 빈 객체 안에 변수들을 추가 해 넣는다.
 -&gt; 용도에 따라서 객체를 생성하고, 변수를 그룹화하기 위해서 사용한다.</p>
</li>
<li><p>함수의 추가
 -&gt; 빈 객체 안에 함수들을 추가 해 넣는다.
 -&gt; 기능은 서로 다르지만, 용도가 비슷한 함수들을 하나의 그룹으로 묶기 위한 단위가 객체이다.</p>
</li>
</ul>
<h2 id="7-2-빈-객체의-생성">7-2. 빈 객체의 생성</h2>
<pre><code>let people = {}</code></pre><h2 id="7-3-변수의-추가">7-3. 변수의 추가</h2>
<ul>
<li><p>객체안에 추가되어 있는 변수를 멤버변수 혹은 프로퍼티 라고 한다.</p>
</li>
<li><p>변수를 추가하기 위해서는 객체이름.변수명 = 값의 형태를 사용한다.</p>
</li>
<li><p>선언을 위한 별도의 키워드는 사용되지 않는다.</p>
<p> people.name = &quot;자바학생&quot;;
 people.age = 20;</p>
</li>
</ul>
<h1 id="8-string">8. String</h1>
<h1 id="9-웹-브라우저와-javascipt">9. 웹 브라우저와 Javascipt</h1>
<ul>
<li>HTML : 정보</li>
<li>CSS : 디자인</li>
<li>JavaScript : 웹 브라우저 HTML 제어</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[HTML 2일차]]></title>
            <link>https://velog.io/@kyj-8754/HTML-2%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@kyj-8754/HTML-2%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Tue, 08 Aug 2023 04:50:25 GMT</pubDate>
            <description><![CDATA[<h1 id="1-파일-찾아보기">1. 파일 찾아보기</h1>
<h2 id="1-1-파일-찾아보기">1-1. 파일 찾아보기</h2>
<ul>
<li>type=&quot;file&quot;요소는 웹 프로그램으로 파일을 전송할 수 있도록 찾아보기 버튼을 표시하며, 한 번에 하나의 파일만 첨부 가능하다</li>
<li>단, 이 요소가 사용될 경우에는 반드시 <form>태그에 enctype=&quot;multipart/form-data&quot; 속성이 명시 되어야한다. </li>
<li>브라우저의 보안을 위해 이 요소에서는 value속성값을 지정할 수 없다.</li>
</ul>
<h1 id="2-프로그램-전송">2. 프로그램 전송</h1>
<h2 id="2-1-method-속성은-전송방식을-의미한다">2-1. method 속성은 전송방식을 의미한다.</h2>
<ul>
<li>get : 입력된 모든 내용이 URL에 포함되어 전송된다.(기본값)<ul>
<li>post : 입력된 모든 내용이 URP에 노출되지 않고 전송된다.</li>
</ul>
</li>
</ul>
<h1 id="3-sementic-tag">3. sementic tag</h1>
<ul>
<li><p>의미론적 tag</p>
</li>
<li><p>문서의 정보를 보다 잘 표현하기 위해서는 의미에 맞는 태그를 잘 사용해야 한다. 특히 HTML5에서는 웹페이지에서 통상 많이 사용하는 구조에 의미를 분명히 부여하기 위해서 의미론적 태그(semantic element)를 새롭게 정의해서 제공하고있다.</p>
</li>
<li><p>SEO(Search Engine Optimiztion)
 -&gt; 검색을 최적화하기 위해서 우리가 제목, 부제목, 시멘틱 태그를 잘 활용한다면, 특정 키워드로 검색 했을 때, 내가 만든 웹사이트가 검색창에 노출될 수 있다.
 -&gt; 다시 말해서 검색 엔진이 내가 만든 웹사이트를 어떤 결과에 띄워줘야 될 지를 알려줄 수 있다.</p>
</li>
<li><p>유지보수성
 -&gt; 단순히 div tag로만 모든 구조를 짜는 것보다 더 한눈에 알아볼 수 있기 때문에, 다른 개발자들이 코드를 유지보수 하기가 더 편해진다.</p>
</li>
</ul>
<h1 id="4-css">4. CSS</h1>
<h2 id="4-1-css란">4-1. CSS란</h2>
<ul>
<li>Cascading Style Sheet</li>
<li>HTML태그에 옷(=디자인)을 입혀주는 기법</li>
<li>문서 전체의 일관성을 유지할 수 있고, 세세한 스타일 지저의 필요를 줄여준다.</li>
</ul>
<h1 id="5-css-셀렉터">5. CSS 셀렉터</h1>
<h2 id="5-1-기본-셀렉터-종류">5-1. 기본 셀렉터 종류</h2>
<ul>
<li>CSS에서 셀렉터를 작성하는 방법은 기본적으로 HTML의 태그이름, 클래스 속성, id속성에 대한 명시가 있다.</li>
</ul>
<h2 id="5-2-태그이름">5-2. 태그이름</h2>
<ul>
<li>selector{... css 본문 ...}</li>
<li>특정 태그를 가리킨다. HTML내에 동일 태그가 존재할 경우 모든 태그 요소를 일괄 처리 한다.</li>
</ul>
<h2 id="5-3-클래스">5-3. 클래스</h2>
<ul>
<li>.selector{... css 본문...}</li>
<li>셀렉터 이름 앞에 점(.)을 붙여 표시하고 HTML 태그의 class속성에 점을 제외한 이름을 명시하여 지정한다.</li>
<li>태그의 종류를 가지지 않고 여러 요소에 복수 지정이 가능하다.
(재사용의 목적)</li>
<li>이 경우 selector의 이름은 자유롭게 지정한다.</li>
</ul>
<h2 id="5-4-id">5-4. id</h2>
<ul>
<li>#selector{...css본문...}</li>
<li>셀렉터 이름앞에 #을 붙여 표시하고, HTML태그의 id속성에 #을 제외한 이름을 명시하여 지정한다.</li>
<li>id속성의 값은 HTML페이지 내에서 중복사용될 수 없다
(고유특성 명시)</li>
<li>이 경우 selector의 이름은 자유롭게 지정한다.</li>
</ul>
<h2 id="5-5-조합형-셀렉터">5-5. 조합형 셀렉터</h2>
<ul>
<li>tag.class{.. css 값...}</li>
<li>tag#id{...css 값...}</li>
<li>id값이나 class값을 태그 이름과 함께 명시하여 적용하는 형태</li>
<li>셀렉터가 지정하는 대상을 자세하게 명시할 수 있다.</li>
<li>태그,클래스,아이디를 독립적으로 명시할 때보다 우선적으로 적용된다.</li>
</ul>
<h2 id="5-6-and의-으미리르-갖는-콤마-사용하기">5-6. and의 으미리르 갖는 콤마(,) 사용하기</h2>
<ul>
<li>selector1, selector2, selector3, ... {...css 적용...}</li>
<li>여러 개의 요소에 동일한 CSS내용을 적용하기 위해 셀렉터를 콤마(,)로 구분하여 일괄 처리 할 수 있다.</li>
<li>태그, class, id 등 모든 형태의 CSS셀렉터가 가능하다.</li>
</ul>
<h1 id="6-링크와-관련된-가상-클래스-선택자">6. 링크와 관련된 가상 클래스 선택자</h1>
<ul>
<li>HTML태그에서 특정 상황이 발생했을 경우에만 적용되는 CSS셀렉터</li>
<li>가상클래스는 CSS의 셀렉터 이름 뒤에 &quot;:상황&quot;의 형식으로 명시한다.</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>