<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>jmsuper_97.log</title>
        <link>https://velog.io/</link>
        <description>블로그 이전 : https://blog.naver.com/tjsqls2067</description>
        <lastBuildDate>Mon, 05 Dec 2022 08:17:46 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>jmsuper_97.log</title>
            <url>https://velog.velcdn.com/images/jmsuper_97/profile/8fee9727-81af-485e-977c-12d1a8cabb7d/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. jmsuper_97.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/jmsuper_97" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[스프링] 3. 웹 앱 개발 - SpringJDBC]]></title>
            <link>https://velog.io/@jmsuper_97/%EC%8A%A4%ED%94%84%EB%A7%81-3.-%EC%9B%B9-%EC%95%B1-%EA%B0%9C%EB%B0%9C-SpringJDBC</link>
            <guid>https://velog.io/@jmsuper_97/%EC%8A%A4%ED%94%84%EB%A7%81-3.-%EC%9B%B9-%EC%95%B1-%EA%B0%9C%EB%B0%9C-SpringJDBC</guid>
            <pubDate>Mon, 05 Dec 2022 08:17:46 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>참고링크</p>
</blockquote>
<ul>
<li><a href="https://www.tutorialspoint.com/spring/spring_jdbc_framework.htm">https://www.tutorialspoint.com/spring/spring_jdbc_framework.htm</a></li>
<li><a href="https://www.baeldung.com/java-connection-pooling">https://www.baeldung.com/java-connection-pooling</a></li>
</ul>
<h2 id="springjdbc">SpringJDBC</h2>
<ul>
<li>JDBC 프로그래밍에서 반복적으로 나타나는 부분을 spring이 수행해준다.</li>
<li>JDBC의 모든 저수준 세부사항을 스프링 프레임워크가 처리해준다.</li>
</ul>
<br>

<h3 id="spring-jdbc-패키지">Spring JDBC 패키지</h3>
<ul>
<li><p>org.springframework.jdbc.core
  jdbc 템플릿 클래스와 다양한 콜백 인터페이스를 포함하며 관련 클래스를 포함한다.</p>
</li>
<li><p>org.springframework.jdbc.datasource
  datasource에 접근을 쉽게하는 유틸리티 클래스와 datasource 구현체 등을 제공한다.</p>
</li>
<li><p>org.springframework.jdbc.object
  RDBMS의 조회, 갱신, 저장등을 객체로 제공</p>
</li>
<li><p>org.springframework.jdbc.support
 sql exception 변환 기능과 약간의 유틸리티 클래스를 제공하고 있다.</p>
</li>
</ul>
<br>

<h3 id="jdbc-template">JDBC Template</h3>
<ul>
<li>spring jdbc core에서 가장 중요한 클래스</li>
<li><strong>리소스 생성, 소멸을 관리</strong></li>
<li>스테이트먼트의 생성과 실행을 처리</li>
<li>sql 조회, 업데이트 저장 프로시저를 호출하고 resultSet 반복호출 등을 실행</li>
<li>JDBC 예외 발생 시 org.springframework.dao패키지에 정의되어 있는 일반적인 예외로 변환</li>
</ul>
<p><br><br></p>
<h2 id="springjdbc와-raw-jdbc-비교">SpringJDBC와 raw JDBC 비교</h2>
<h3 id="springjdbc-1">SpringJDBC</h3>
<ul>
<li>org.springframework.jdbc.datasource.<strong>DriverManagerDataSource</strong> 클래스<ul>
<li>java.sql.DataSource의 구현체</li>
<li>커넥션풀을 사용하지 않음 → 매 요청 시 새로운 Connection 객체 생성 → <strong>실서버에서 사용하기에는 부적절</strong></li>
<li>순수 JDBC에서 DriverManager 클래스를 이용해 Connection 객체를 생성하는 프로세스를 담당</li>
<li><code>클래스 주석 : Simple implementation of the standard JDBC javax.sql.DataSource interface, configuring the plain old JDBC DriverManager via bean properties, and returning a new Connection from every getConnection call.</code></li>
</ul>
</li>
</ul>
<ul>
<li>javax.sql.<strong>DataSource</strong> 인터페이스<ul>
<li>위 클래스가 대표하는(대신하는) 물리적 데이터 소스에 대한 커넥션을 생성하는 factory</li>
<li>DriverManager의 대안으로, DB에 대한 커넥션을 가져오는 도구로 추천됨</li>
<li>위 인터페이스는 드라이버 벤더에 의해 구현된다. 아래와 같은 세 가지 종류의 구현 방법이 있다.<ol>
<li>기본 구현 : 표준 Connection 객체를 제공</li>
<li>커넥션 풀링 구현 : 커넥션 풀에 있는 Connection 객체를 제공. 이 구현법은 중간에 pooling manager를 통해 동작</li>
<li>분산된 트랜잭션 구현 : 분산된 트랜잭션을 위해 사용되고 커넥션풀에 포함된 Connection 객체를 제공. 이 구현법은 중간에 transaction manager와 pooling manager를 통해 동작</li>
</ol>
</li>
<li>DataSource의 property는 수정이 가능하기 때문에, DataSource가 다른 서버로 이동되는 경우, 기존 소스코드를 수정할 필요는 없다.</li>
</ul>
</li>
</ul>
<p><code>SpringJdbcConfig.java</code></p>
<pre><code class="language-java">@Configuration
@ComponentScan
public class SpringJdbcConfig {

    @Bean
    public DataSource mysqlDataSource(){
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(&quot;com.mysql.cj.jdbc.Driver&quot;);
        dataSource.setUrl(&quot;jdbc:mysql://localhost:3306/spring&quot;);
        dataSource.setUsername(&quot;root&quot;);
        dataSource.setPassword(&quot;1111&quot;);

        return dataSource;
    }
}</code></pre>
<p><code>SpringJdbcRepository.java</code></p>
<pre><code class="language-java">@Repository
public class SpringJdbcRepository {

    private DataSource dataSource;

    public SpringJdbcRepository(DataSource dataSource){
        this.dataSource = dataSource;
    }

    public int getUserCount(){
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        int result = jdbcTemplate.queryForObject(
                &quot;SELECT COUNT(*) FROM USER&quot;, Integer.class
        );
        return result;
    }
}</code></pre>
<p><code>SpringJdbcTest.java</code></p>
<pre><code class="language-java">public class SpringJdbcTest {
    public static void main(String[] args) {
        ApplicationContext ac = new AnnotationConfigApplicationContext(SpringJdbcConfig.class);
        SpringJdbcRepository repository = ac.getBean(&quot;springJdbcRepository&quot;,SpringJdbcRepository.class);
        int queryResult = repository.getUserCount();
        System.out.println(queryResult);
    }
}</code></pre>
<br>

<h3 id="raw-jdbc">raw JDBC</h3>
<ul>
<li><p>의문점</p>
<ul>
<li><p>getConnection() 메소드를 통해 DB와 연결된 Connection 객체를 반환받는다. 이때 connection 객체는 새로 생성되는 것일까? 아니 ConnectionPool에 있는 객체의 주소를 받는 것일까?</p>
<p><strong>새로운 커넥션을 생성한다. 커넥션풀을 사용하려면, 따로 설정해야 한다.</strong></p>
</li>
<li><p>만약 커넥션 객체를 새로 생성한다면, Connection은 내부적으로 소켓으로 통신하는가? 어떤 통신 기법을 사용하는가?</p>
<p><strong>connection은 서버에서 사용중인 DB에서 정의한 프로토콜을 기반으로 통신한다. 예를 들어, Mysql은 TCP/IP socket을 기반으로 통신하는 프로토콜을 사용한다. 따라서, connection이라 함은 socket을 통해 mysql과 연결되는 객체임을 알 수 있다.</strong></p>
<ul>
<li><p>mysql protocol : <a href="https://dev.mysql.com/doc/dev/mysql-server/latest/PAGE_PROTOCOL.html">https://dev.mysql.com/doc/dev/mysql-server/latest/PAGE_PROTOCOL.html</a></p>
</li>
<li><p>커넥션 객체 불일치 테스트 코드</p>
<pre><code class="language-java">public class RawJdbcTest {
  public static void main(String[] args) throws SQLException, ClassNotFoundException {
      connectionCreateTest();
  }

  public static void connectionCreateTest() throws SQLException, ClassNotFoundException {
      Connection connection1 = RawJdbcConfig.getConnection();
      Connection connection2 = RawJdbcConfig.getConnection();

      if(connection1 != connection2){
          System.out.println(&quot;서로 다른 커넥션 객체를 생성합니다.&quot;);
      }
  }
}
// 출력 : 서로 다른 커넥션 객체를 생성합니다.</code></pre>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p><code>RawJdbcConfig.java</code></p>
<pre><code class="language-java">public class RawJdbcConfig {
    public static Connection getConnection() throws ClassNotFoundException, SQLException {
        Class.forName(&quot;com.mysql.cj.jdbc.Driver&quot;);

        String dburl = &quot;jdbc:mysql://localhost:3306/spring?useSSL=false&quot;;
        String ID = &quot;root&quot;;
        String PWD = &quot;1111&quot;;

        Connection conn = DriverManager.getConnection(dburl,ID,PWD);
        return conn;
    }
}</code></pre>
<p><code>RawJdbcTest.java</code></p>
<pre><code class="language-java">public class RawJdbcTest {
    public static void main(String[] args) throws SQLException, ClassNotFoundException {
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        try {
            conn = RawJdbcConfig.getConnection();
            stmt = conn.createStatement();
            rs = stmt.executeQuery(&quot;select id from USER;&quot;);
            while(rs.next()) System.out.println(rs.getInt(&quot;id&quot;));
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if(rs != null) {
                rs.close();
            }
            if(stmt != null) {
                stmt.close();
            }
            if(conn != null) {
                conn.close();
            }
        }
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[스프링] 3. 웹 앱 개발 - SpringCore]]></title>
            <link>https://velog.io/@jmsuper_97/%EC%8A%A4%ED%94%84%EB%A7%81-3.-%EC%9B%B9-%EC%95%B1-%EA%B0%9C%EB%B0%9C-SpringCore</link>
            <guid>https://velog.io/@jmsuper_97/%EC%8A%A4%ED%94%84%EB%A7%81-3.-%EC%9B%B9-%EC%95%B1-%EA%B0%9C%EB%B0%9C-SpringCore</guid>
            <pubDate>Mon, 28 Nov 2022 07:31:59 GMT</pubDate>
            <description><![CDATA[<h2 id="spring-framework란">Spring Framework란?</h2>
<ul>
<li>엔터프라이즈급 어플리케이션을 구축할 수 있는 솔루션</li>
<li>원하는 부분만 가져다 사용할 수 있도록 모듈화가 잘 되어 있다. 따라서, 필요한 모듈만 가져와 사용하면 된다.</li>
<li>IoC 컨테이너이다. 번역하면 역전 제어 컨테이너 인데, 어플리케이션에서 사용하는 도구들을 앱 단에서 능동적으로 생성하지 않고, 스프링이 생성 및 관리하는 것을 말한다.</li>
<li>선언적으로 트랜잭션을 관리할 수 있다.</li>
<li>완전한 기능을 갖춘 MVC Framework를 제공</li>
<li>AOP를 지원한다. AOP는 관점 지향 프로그래밍을 의미하며, 코드상에 중복해서 사용되는 부분들을 모듈화하여 비즈니스 로직으로 부터 분리하는 것을 말한다.</li>
</ul>
<p><br><br></p>
<h2 id="스프링의-핵심--빈-팩토리">스프링의 핵심 : 빈 팩토리</h2>
<p>스프링의 핵심은 빈 팩토리 또는 애플리케이션 컨텍스트라고 불리는 것이다. 이 두가지는 DaoFactory를 일반화한 것이다.
스프링에서는 애플리케이션 컨텍스트를 IoC 컨테이너라 하기도 하고, 스프링 컨테이너라고 하기도 한다. 또는 빈 팩토리라고 부르기도한다.</p>
<ul>
<li>빈 팩토리 : 스프링에서 빈의 생성과 관계설정 같은 제어를 담당하는 IoC 오브젝트.</li>
<li>애플리케이션 컨텍스트 : 빈 팩토리를 좀더 확장한 것</li>
</ul>
<p>스프링은 여러 번에 걸쳐 빈을 요청하더라도 매번 동일한 오브젝트를 돌려준다. 애플리케이션 컨텍스트는 싱글톤을 저장하고 관리하는 싱글톤 레지스트리이다.</p>
<p>싱글톤 레지스트리는 일반적인 싱글톤 패턴과 다르다. 싱글톤 패턴은 private 생성자를 사용하기 때문에 상속이 불가능하다. 또한 만들어지는 방식이 제한적이여서 테스트가 힘들다.</p>
<p>그 외에도 서버에서 싱글톤 1개만 생성되는 것을 보장하지 않으며, 객체 지향관점에서 전역 상태를 갖는것은 바람직하지 않다. </p>
<p>싱글톤 레지스트리는 이러한 단점들을 해결해준다. 빈을 생성하는 클래스는 public생성자를 갖는다. 다만, 스프링이 빈이 싱글톤으로 생성되도록 관리하는 것이다.</p>
<p>스프링의 싱글톤 빈으로 사용되는 클래스를 만들 때는 기존의 UsreDao처럼 개별적으로 바뀌는 정보는 로컬 변수로 정의하거나, 파라미터로 주고받으면서 사용하게 해야 한다.</p>
<p>만약 멤버변수로 정의할 경우 멀티쓰레드 환경에서 데이터가 뒤죽박죽이 되는 상황에 직면할 수 있다. 만약 자신이 사용하는 다른 싱글톤 빈을 저장하려는 용도라면 인스턴스 변수를 사용해도 좋다. 또한 일기전용의 변수도 인스턴스 변수로 사용해도 된다.</p>
<br>

<h3 id="빈bean이란">빈(Bean)이란?</h3>
<p>스프링에서는 스프링이 제어권을 가지고 직접 만들고 관계를 부여하는 오브젝트를 빈(bean)이라고 부른다. 자바빈 또는 EJB에서 말하는 빈과 비슷한 오브젝트 단위의 애플리케이션 컴포넌트를 말한다. 동시에 스프링 빈은 스프링 컨테이너가 생성과 관계설정, 사용 등을 제어해주는 제어의 역전이 적용된 오브젝트를 가리키는 말이다.</p>
<p>애플리케이션에서 만들어지는 모든 오브젝트가 다 빈은 아니다. 그 중에서 스프링이 직접 그 생성과 제어를 담당하는 오브젝트만을 빈이라고 부른다.</p>
<p><br><br></p>
<h2 id="spring-iocdi-컨테이너">Spring IoC/DI 컨테이너</h2>
<h3 id="컨테이너container란">컨테이너(Container)란?</h3>
<p>컨테이너는 인스턴스의 생명주기를 관리한다. 인스턴스의 생성과 소멸을 다른 누군가가 대신 해주는 것이다.</p>
<p>생성된 인스턴스들에게 추가적인 기능을 제공한다. 톰캣에서 서블릿 컨테이너가 이에 해당한다. 개발자가 서블릿 클래스를 작성하지만, 이를 생성하고 관리하는 것은 서블릿 컨테이너가 하는 일이다. JSP파일 또한, JSP가 서블릿으로 바뀌고 인스턴스가 생성되는 것도 톰캣이 관리하는 것이다.</p>
<br>

<h3 id="ioc란">IoC란?</h3>
<p>개발자는 프로그램의 흐름을 제어하는 코드를 작성한다. 그런데, 이 흐름의 제어를 개발자가 하는 것이 아니라 다른 프로그램이 그 흐름을 제어하는 것을 IoC라고 말한다.</p>
<p>IoC를 사용하므로서 애플리케이션에서 필요로 하는 객체들의 관리를 위임하게 된다. 이로서 개발자는 비즈니스 로직에만 집중할 수 있다. 또한, 코드의 수정사항이 생길 때 직접 객체 생성을 수정할 경우 일이 많아지고, 각각의 객체들간의 관계를 알고 있어야 하기 때문에, 유지보수에서도 장점을 갖는다.</p>
<br>

<h3 id="di란">DI란?</h3>
<p>DI는 클래스 사이의 의존 관계에 대해 빈(Bean) 설정 정보를 바탕으로 컨테이너가 자동으로 연결해주는 것을 말한다.</p>
<pre><code class="language-java">public class Engine{

}

public class Car{
    private Engine engine = null;

    Car(){
        engine = new Engine();
    }
}</code></pre>
<p>위 경우는 DI가 적용되지 않았다. 직접 객체를 생성하고 있다. 만약 사용할 클래스가 달라질 경우 매번 코드를 수정해줘야 한다.</p>
<pre><code class="language-java">@Component
public class Engine{

}

@Component
public class Car{

    @Autowired
    private Engine engine;

}</code></pre>
<p>위 경우는 DI가 적용되었다. 코드에서 직접 객체를 생성할 필요없이 어노테이션을 적용시켜 객체의 관리를 위임하는 것이다.</p>
<br>

<h3 id="spring에서-제공하는-iocdi-컨테이너">Spring에서 제공하는 IoC/DI 컨테이너</h3>
<ul>
<li>BeanFactory : IoC/DI에 대한 기본 기능을 가지고 있다.</li>
<li>ApplicationContext : BeanFactory의 모든 기능을 포함하며, 일반적으로 BeanFactory보다 추천된다. 트랜잭션처리, AOP등에 대한 처리를 할 수 있다. BeanPostProcessor, BeanFactoryPostProcessor등을 자동으로 등록하고, 국제화 처리, 어플리케이션 이벤트 등을 처리할 수 있다.<ul>
<li>BeanPostProcessor : 컨테이너의 기본 로직을 오버라이딩해서 인스턴스화와 로직등을 개발자가 커스텀할 수 있게 하는 것</li>
<li>BeanFactoryPostProcessor : 설정 메타데이터를 커스텀할 수 있게 하는 것</li>
</ul>
</li>
</ul>
<p><br><br></p>
<h2 id="spring-iocdi-실습">Spring IoC/DI 실습</h2>
<h3 id="xml을-이용한-ioc">xml을 이용한 IoC</h3>
<p>xml파일에 Bean의 정보를 입력하여 스프링 프레임워크가 해당 객체를 관리하도록 설정할 수 있다. xml파일은 src/main/resources에 저장한다. 기본적으로 src폴더 하위의 파일들은 자동으로 classPath에 저장된다.
classPath는 말 그대로 JVM에서 클래스 파일을 찾는 데 기준이 되는 경로를 의미한다.</p>
<p><code>src/main/resources/applicationContext.xml</code></p>
<pre><code class="language-xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
    xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
    xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd&quot;&gt;

    &lt;bean id=&quot;userBean&quot; class=&quot;kr.or.connect.diexam01.UserBean&quot;&gt;&lt;/bean&gt;
&lt;/beans&gt;</code></pre>
<p><code>&lt;bean&gt;</code>태그를 사용하여 빈 객체의 정보를 입력한다. id는 getBean()메소드를 통해 빈 객체를 가져올 때 사용되는 이름이며,
class는 해당 빈의 클래스파일을 지정하는 속성이다.</p>
<pre><code class="language-java">public class ApplicationContext01 {

    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext(&quot;classpath:applicationContext.xml&quot;);
        System.out.println(&quot;초기화 완료!!&quot;);

        UserBean userBean = (UserBean)ac.getBean(&quot;userBean&quot;);
        userBean.setName(&quot;강&quot;);

        System.out.println(userBean.getName());

        UserBean userBean2 = (UserBean)ac.getBean(&quot;userBean&quot;);

        if(userBean == userBean2) {
            System.out.println(&quot;같은 인스턴스&quot;);
        }
    }
}</code></pre>
<p><code>ApplicationContext</code>는 인터페이스로 빈을 관리하는 클래스이다. 해당 클래스를 사용하려면 인터페이스를 실제 구현한 클래스들 중 하나를 선택하여 사용한다. 
그 중 <code>ClassPathXmlApplicationContext</code>는 입력받은 xml파일을 확인하여 해당 파일에 담긴 모든 빈들을 메모리에 올려둔다. 
즉, <code>ClassPathXmlApplicationContext</code>의 인스턴스가 생성될 때 xml파일을 확인하여 각각의 Bean들과 Bean들에는 해당하는 classPath를 확인하여 빈 객체를 생성한 후 메모리에 올려주는 것이다.</p>
<p>xml에 지정했던 Bean의 id에 해당하는 이름을 getBean()메소드의 인자로 넣으면 해당하는 빈 객체의 래퍼런스를 반환한다. 그런데 이때 Bean들은 다양한 종류이기 때문에 이를 모두 포함하기 위해 특정 type이 아닌 Object type으로 반환한다. 따라서, getBean()을 통해 반환받은 래퍼런스를 변수에 할당할 때 형 변환을 시켜줘야 한다.</p>
<p>애플리케이션 컨텍스트는 싱글톤 리포지토리이다. 따라서 Bean 객체는 1개만 생성되며 위 코드에서 userBean1과 userBean2는 동일한 Bean 객체를 가리킨다.</p>
<br>

<h3 id="xml을-이용한-di">xml을 이용한 DI</h3>
<p>Engine 클래스의 인스턴스를 멤버 변수로 할당하는 Car 클래스를 정의할 것이다.</p>
<p>Engine.java</p>
<pre><code class="language-java">public class Engine {
    public Engine() {
        System.out.println(&quot;엔진 생성자&quot;);
    }

    public void exec() {
        System.out.println(&quot;엔진이 동작합니다.&quot;);
    }
}</code></pre>
<p>Car.java</p>
<pre><code class="language-java">public class Car {
    private Engine v8;

    public Car() {
        System.out.println(&quot;Car 생성자&quot;);
    }

    public Engine getEngine() {
        return v8;
    }

    public void setEngine(Engine e) {
        this.v8 = e;
    }

    public void run() {
        System.out.println(&quot;엔진을 이용하여 달린다.&quot;);
        v8.exec();
    }

}</code></pre>
<p>위 Car 클래스의 setEngine(Engine e)메소드가 중요하다. 반드시 set~~ 형태로 지정해야 하며, set뒤에 다오는 명칭은 xml파일에 정의된 property의 name과 일치해야 한다.
두 명칭이 일치하지 않으면 어플리케이션 컨텍스트는 해당 Bean을 찾지 못하여 객체를 주입시키지 못할 것이다.</p>
<p>applicationContext.xml</p>
<pre><code class="language-xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
    xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
    xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd&quot;&gt;

    &lt;bean id=&quot;e&quot; class=&quot;kr.or.connect.xmldi.Engine&quot;&gt;&lt;/bean&gt;
    &lt;bean id=&quot;c&quot; class=&quot;kr.or.connect.xmldi.Car&quot;&gt;
        &lt;property name=&quot;engine&quot; ref=&quot;e&quot;&gt;&lt;/property&gt;
    &lt;/bean&gt;
&lt;/beans&gt;</code></pre>
<p>Bean객체안에 Bean객체를 주입시키기 위해 <code>&lt;property&gt;</code>태그를 사용한다. naem은 Car클래스의 property와 일치해야하며, ref는 주입될 Bean이
누구인지 명시해주는 속성이다. 즉, Bean 클래스의 setter와 xml의 <code>&lt;property&gt;</code>가 xml을 이용한 DI의 핵심이다.</p>
<br>

<h3 id="configuration-어노테이션을-이용한-di">Configuration 어노테이션을 이용한 DI</h3>
<p>jdk5부터 xml파일 대신 어노테이션을 이용하여 빈을 등록할 수 있다. Car,Engine클래스는 이전과 동일하다. 달라지는 것은
ApplicationCofig.java파일이 새롭게 생성된다는 것이다. 해당 config파일이 xml을 대신한다.</p>
<p>ApplicationConfig.java</p>
<pre><code class="language-java">@Configuration
public class ApplicationConfiguration {

    @Bean
    public Car car(Engine e) {
        Car c = new Car();
        c.setEngine(e);
        return c;
    }

    @Bean
    public Engine engine() {
        return new Engine();
    }
}</code></pre>
<p><code>@Configuration</code> 어노테이션을 클래스에 적용하면, applicationContext의 인스턴스가 생성될 때 해당 클래스에 등록된 빈들이 생성되게 된다.
그런데 위 코드에서 만약 car 빈이 먼저 생성되면 어떻게 될까?</p>
<p>다행이도 그럴일은 없다. 스프링은 파라미터를 받지 않는 빈들을 우선적으로
생성하고, 해당 빈들이 파라미터로 들어가질 빈들을 생성하게 된다. 빈 메소드들은 반드시 메소드에서 반환되는 클래스 명칭과 일치할 필요는 없다.</p>
<p>테스트해보니 이름이 다르더라도 빈은 잘 주입되고 있었다. 아마도, 스프링은 빈 메소드를 빈을 부르기 위한 명칭으로 사용하지만, 파라미터로
사용되는 빈을 찾을 때는 빈 메소드 명칭이 아닌 클래스 명칭으로 찾는 것으로 추측된다.</p>
<br>

<h3 id="componentscan-어노테이션을-이용한-di">ComponentScan 어노테이션을 이용한 DI</h3>
<p><code>@ComponentScan</code>어노테이션은 파라미터로 지정한 경로의 패키지의 클래스들 중에서 @Component 혹은 service, repository 등등의 어노테이션이 적용된 클래스들을 빈 객체로 생성시켜주는 어노테이션이다. 그렇다면, config파일에 직접 bean을 설정하는 것은 필요없는 것일까?</p>
<p>그렇지 않다. 라이브러리를 가져와 사용할 때 해당 클래스를 빈으로 등록해야 할 경우가 있다. 이 경우 라이브러리에 들어가 어노테이션을
적용시킬 수 없기에 이러한 경우 직접 config파일에서 빈으로 등록해야 한다.</p>
<p>@Autowired어노테이션이 적용된 멤버 변수는 스프링에 의해 자동으로 빈 객체 주입될 것이다. 따라서, setter가 없어도 알아서 주입시켜준다.</p>
<p>Engine.java</p>
<pre><code class="language-java">@Component
public class Engine {
    public Engine() {
        System.out.println(&quot;Engine 생성자&quot;);
    }

    public void exec() {
        System.out.println(&quot;엔진이 동작합니다.&quot;);
    }

}</code></pre>
<p>Car.java</p>
<pre><code class="language-java">@Component
public class Car {
    @Autowired
    private Engine v8;

    public Car() {
        System.out.println(&quot;Car 생성자&quot;);
    }

    public void run() {
        System.out.println(&quot;엔진을 이용하여 달립니다.&quot;);
        v8.exec();
    }
}</code></pre>
<p>ApplicationCofig.java</p>
<pre><code class="language-java">@Configuration
@ComponentScan(&quot;kr.or.connect.componentscandi&quot;)
public class ApplicationConfig {

}
ApplicationContext.java

public class ApplicationContextExam4 {
    public static void main(String[] args) {
    ApplicationContext ac = new AnnotationConfigApplicationContext(ApplicationConfig.class);

        Car car = (Car)ac.getBean(Car.class);
        car.run();
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[스프링] 3. 웹 앱 개발 - IoC with Java & Spring]]></title>
            <link>https://velog.io/@jmsuper_97/%EC%8A%A4%ED%94%84%EB%A7%81-3.-%EC%9B%B9-%EC%95%B1-%EA%B0%9C%EB%B0%9C-IoC-with-Java-Spring</link>
            <guid>https://velog.io/@jmsuper_97/%EC%8A%A4%ED%94%84%EB%A7%81-3.-%EC%9B%B9-%EC%95%B1-%EA%B0%9C%EB%B0%9C-IoC-with-Java-Spring</guid>
            <pubDate>Sun, 27 Nov 2022 10:00:36 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>참고자료</p>
</blockquote>
<ul>
<li>토비의 스프링 3.1 1장_오브젝트와 의존관계</li>
<li>토비의 스프링 3.1 (95p~110p)</li>
</ul>
<br>

<h2 id="순수-자바를-사용한-ioc-예제">순수 자바를 사용한 IoC 예제</h2>
<p>일반적으로 프로그램의 흐름은 main() 메소드와 같이 프로그램이 시작되는 지점에서 다음에 사용할 오브젝트를 결정하고, 만들어진 오브젝트의 메소드를 호출하는 과정들을 반복한다. 즉, 자신이 사용할 오브젝트를 자신이 직접 결정하고 생성한다. </p>
<p>그러나 IoC(제어의 역전)에서는 이 제어의 흐름이 거꾸로 뒤집힌다. 제어의 역전에서는 오브젝트가 자신이 사용할 오브젝트를 스스로 선택하지 않는다. 모든 제어 권한을 자신이 아닌 다른 대상에게 위임한다. main()을 제외하고 모든 오브젝트에서는 이렇게 위임받은 제어 권한을 갖는 특별한 오브젝트에 의해 결정된다. IoC의 예시로는 서블릿, 템플릿 메소드 패턴, 프레임워크 등이 있다.</p>
<br>

<h3 id="예제-코드-설명">예제 코드 설명</h3>
<p><code>UserDao</code> : DB에 접근하여 <code>User</code>모델을 다루는 클래스</p>
<p><code>ConnectionMaker</code> : DB연결을 처리하는 클래스를 만드는 인터페이스</p>
<p><code>DConnectionMaker</code> : <code>ConnectionMaker</code>의 구현체로, DB와 관련한 실제 정보를 담고있는 클래스</p>
<p><code>DaoFactory</code> : ConnectionMaker의 구현체를 생성하여 이를 Dao에 주입하여 객체 생성후 Dao를 제공하는 클래스</p>
<p><code>UserDaoTest</code> : UserDao의 인스턴스를 사용하여 실행 테스트하는 클래스</p>
<p>위 코드들은 IoC 구조를 가지고 있다. UserDao클래스는 <code>데이터 접근</code>에만 관심을 두며 <code>DB접속</code>에는 관심을 두지 않는다. DB접속을 담당하는 Connection의 객체를 필요로 하지만, 직접 생성하지 않고 <code>ConnectionMaker</code> 인터페이스에게 위임한다. 권한을 위임함으로써 DB접근 코드 수정 시 UserDao의 코드는 수정되지 않는다. 다만, ConnectionMaker를 구현한 구현체의 코드만 달라질 뿐이다.</p>
<p><img src="https://github.com/JMsuper/TIL/raw/main/Spring/img/IoC_ex.JPG" alt="https://github.com/JMsuper/TIL/raw/main/Spring/img/IoC_ex.JPG"></p>
<p><code>Connection c = connectionMaker.makeConnection();</code></p>
<p>또한, main()함수를 실행시키는 <code>UserDaoTest</code> 클래스는 직접 UserDao의 객체를 생성하지 않는다. UserDao의 객체를 생성하려면, UserDao의 생성자에 ConnectionMaker의 구현체를 주입시켜야 한다.</p>
<p> 만약 직접 주입한다면, UserDao의 기능을 테스트하기 위한 목적으로 설계된 UserDaoTest의 기능이 흘들리게 된다. 또 다른 책임까지 떠맡고 있는 것은 문제가 있다. 따라서 <code>DaoFactory</code>클래스를 통해 UserDao와 ConnectionMaker 구현 클래스의 오브젝트를 만드는 것과,그렇게 만들어진 두 개의 오브젝트가 연결돼서 사용될 수 있도록 관계를 맺게 한다.</p>
<h3 id="예제-코드">예제 코드</h3>
<ul>
<li><p>User.java</p>
<pre><code class="language-java">public class User {
  String id;
  String name;
  String password;

  public String getId(){
      return id;
  }
  public void setId(String id) {
      this.id = id;
  }
  public String getName(){
      return name;
  }
  public void setName(String name){
      this.name = name;
  }
  public String getPassword(){
      return password;
  }
  public void setPassword(String password) {
      this.password = password;
  }
}
</code></pre>
</li>
</ul>
<pre><code>- UserDao.java
``` java
/**
 * DB에 접근하여 User모델을 처리하는 클래스
 */
public class UserDao {

    private ConnectionMaker connectionMaker;

    public UserDao(ConnectionMaker connectionMaker){
        this.connectionMaker = connectionMaker;
    }

    public void add(User user) throws SQLException, ClassNotFoundException {
        Connection c = connectionMaker.makeConnection();
        PreparedStatement ps = c.prepareStatement(
                &quot;insert into user(id,name,password) values(?,?,?)&quot;
        );
        ps.setString(1,user.getId());
        ps.setString(2,user.getName());
        ps.setString(3,user.getPassword());

        ps.executeUpdate();

        ps.close();
        c.close();
    }

    public User get(String id) throws ClassNotFoundException, SQLException{
        Connection c = connectionMaker.makeConnection();

        PreparedStatement ps = c.prepareStatement(
                &quot;select * from user where id = ?&quot;);
        ps.setString(1,id);

        ResultSet rs = ps.executeQuery();
        rs.next();
        User user = new User();
        user.setId(rs.getString(&quot;id&quot;));
        user.setName(rs.getString(&quot;name&quot;));
        user.setName(rs.getString(&quot;password&quot;));

        rs.close();
        ps.close();
        c.close();

        return user;
    }
}
</code></pre><ul>
<li><p>ConnectionMaker.java</p>
<pre><code class="language-java">/**
* DB 연결을 처리하는 클래스를 만드는 인터페이스
*/
public interface ConnectionMaker {
  Connection makeConnection() throws ClassNotFoundException, SQLException;
}</code></pre>
</li>
<li><p>DConnectionMaker.java</p>
<pre><code class="language-java">/**
* ConnectionMaker의 구현체로, DB와 관련한 실제 정보를 담고있는 클래스
*/
public class DConnectionMaker implements ConnectionMaker{
  @Override
  public Connection makeConnection() throws ClassNotFoundException, SQLException {
      Class.forName(&quot;com.mysql.cj.jdbc.Driver&quot;);
      Connection c = DriverManager.getConnection(&quot;jdbc:mysql://localhost/spring?useSSL=false&quot;,
              &quot;root&quot;,
              &quot;1111&quot;);
      return c;
  }
}</code></pre>
</li>
<li><p>DaoFactory.java</p>
<pre><code class="language-java">/**
* ConnectionMaker의 구현체를 생성하여 이를 DAO에 주입하여 객체 생성 후 DAO를 제공하는 클래스
*/
public class DaoFactory {
  public UserDao userDao(){
      UserDao userDao = new UserDao(connectionMaker());
      return userDao;
  }

  public ConnectionMaker connectionMaker(){
      return new DConnectionMaker();
  }
}</code></pre>
</li>
<li><p>UserDaoTest.java</p>
<pre><code class="language-java">/**
* UserDao의 인스턴스를 사용하여 실행 테스트하는 클래스
*/
public class Main {
  public static void main(String[] args) throws SQLException, ClassNotFoundException {
      UserDao dao = new DaoFactory().userDao();

      User user = new User();
      user.setId(&quot;1&quot;);
      user.setName(&quot;KJM&quot;);
      user.setPassword(&quot;123&quot;);

      dao.add(user);

      System.out.println(user.getId() + &quot;등록 성공&quot;);

      User user2 = dao.get(user.getId());
      System.out.println(user2.getName());
      System.out.println(user2.getId() + &quot;조회 성공&quot;);
  }
}</code></pre>
<p><br><br></p>
</li>
</ul>
<h2 id="스프링을-이용한-ioc-예제">스프링을 이용한 IoC 예제</h2>
<h3 id="spring-ioc">Spring IoC</h3>
<p>스프링의 핵심은 빈 팩토리(Bean factory) 또는 애플리케이션 컨텍스트(Application context)이다. 이 두 가지는 <code>IoC_with_Java</code>의 <code>DaoFactory</code>를 일반화한 것으로 볼 수 있다.</p>
<p> 스프링에서는 스프링이 제어권을 가지고 직접 만들고 관계를 부여하는 오브젝트를 빈(bean)이라고 부른다. 자바빈 또는 EJB에서 말하는 빈과 비슷한 오브젝트 단위의 애플리케이션 컴포넌트를 말한다. 동시에 스프링 빈은 스프링 컨테이너가 생성과 관계설정, 사용 등을 제어해주는 IoC(제어의 역전)가 적용된 오브젝트를 가리키는 말이다.</p>
<ul>
<li>빈 팩토리 : 스프링에서 빈의 생성과 관계설정 같은 제어를 담당하는 IoC 오브젝트.</li>
<li>애플리케이션 컨텍스트 : 빈 팩토리를 좀더 확장한 것</li>
</ul>
<p>스프링에서는 애플리케이션 컨텍스트를 IoC 컨테이너라 하기도 하고, 스프링 컨테이너라고 하기도 한다. 애플리케이션 컨텍스트는 빈 팩토리를 상속했기 때문에 빈 팩토리라고 부르기도한다. </p>
<p><code>@Configuration</code>이 붙은 DaoFactory는 이 애플리케이션 컨텍스트가 활용하는 IoC 설정정보이다. 내부적으로는 애플리케이션 컨텍스트가 DaoFactory의 userDao() 메소드를 호출해서 오브젝트를 가져온 것을 클라이언트가 getBean()으로 요청할 때 전달해준다.</p>
<p>단편적으로 볼 때에는 순수 자바로 구현한 오브젝트 팩토리와 애플리케이션 컨텍스트가 차이가 없는 것처럼 보일 수 있다.</p>
<p>그러나, 애플리케이션 컨텍스트는 다음과 같은 장점을 가지고 있다.</p>
<ul>
<li>클라이언트는 구체적인 팩토리 클래스를 알 필요가 없다.(클라이언트는 Bean의 이름만 알면된다.)</li>
<li>애플리케이션 컨텍스트는 종합 IoC 서비스를 제공해준다.</li>
<li>애플리케이션 컨텍스트는 빈을 검색하는 다양한 방법을 제공한다.</li>
</ul>
<p>또한 스프링은 여러 번에 걸쳐 빈을 요청하더라도 매번 동일한 오브젝트를 돌려준다. 애플리케이션 컨텍스트는 싱글톤을 저장하고 관리하는 싱글톤 레지스트리이다. 싱글톤 레지스트리는 일반적인 싱글톤 패턴과 다르다.</p>
<p>싱글톤 패턴은 private 생성자를 사용하기 때문에 상속이 불가능하다. 또한 만들어지는 방식이 제한적이여서 테스트가 힘들다. 그 외에도 서버에서 싱글톤 1개만 생성되는 것을 보장하지 않으며, 객체 지향관점에서 전역 상태를 갖는것은 바람직하지 않다.</p>
<p>싱글톤 레지스트리는 이러한 단점들을 해결해준다. 빈을 생성하는 클래스는 public 생성자를 갖는다. 다만, 스프링이 빈을 싱글톤으로 생성되도록 관리하는 것이다. 스프링의 싱글톤 빈으로 사용되는 클래스를 만들 때는 기존의 UsreDao처럼 개별적으로 바뀌는 정보는 로컬 변수로 정의하거나, 파라미터로 주고받으면서 사용하게 해야 한다. 만약 멤버변수로 정의할 경우 멀티쓰레드 환경에서 데이터가 뒤죽박죽이 되는 상황에 직면할 수 있다.</p>
<p> 만약 자신이 사용하는 다른 싱글톤 빈을 저장하려는 용도라면 인스턴스 변수를 사용해도 좋다. 또한 읽기전용의 변수도 인스턴스 변수로 사용해도 된다.</p>
<h3 id="코드-설명">코드 설명</h3>
<p>소스코드는 <code>IoC_with_Java</code>와 거의 유사하다. 다만, <code>DaoFactory</code>와 <code>UserDaoTest</code>가 다르다.</p>
<p>DaoFactory.java</p>
<pre><code class="language-java">@Configuration // 애플리케이션 컨텍스트 또는 빈 팩토리가 사용할 설정정보라는 표시
public class DaoFactory {
    @Bean // 오브젝트 생성을 담당하는 IoC용 메소드라는 표시
    public UserDao userDao(){
        UserDao userDao = new UserDao(connectionMaker());
        return userDao;
    }

    @Bean
    public ConnectionMaker connectionMaker(){
        return new DConnectionMaker();
    }
}</code></pre>
<p>코드는 동일하지만, 어노테이션을 붙인다는 점에서 차이가 있다. <code>@Configuration</code>은 애플리케이션 컨텍스트 혹은 빈 팩토리가 사용할 설정정보라는 표시이다. <code>@Bean</code>은 오브젝트 생성을 담당하는 IoC용 메소드라는 표시이다. 해당 어노테이션을 붙임으로서 스프링 컨테이너는 클라이언트가 <code>getBean()</code>을 통해 Bean 호출시, 해당 클래스와 메소드를 찾거나 빈 목록을 검색하게 된다.</p>
<p>UserDaoTest.java</p>
<pre><code class="language-java">ApplicationContext context =
new AnnotationConfigApplicationContext(DaoFactory.class);
UserDao dao = context.getBean(&quot;userDao&quot;,UserDao.class);</code></pre>
<p><code>DaoFactory</code>로 부터 직접적으로 UserDao 객체를 받던 코드는 애플리케이션 컨텍스트에 있는 Bean을 받는 것으로 수정되었다. <code>userDao</code>는 애플리케이션 컨텍스트에 저장된 bean의 이름이며, 이는 UserDao의 객체를 리턴하는 메소드의 이름이기도 하다. 원래 <code>getBean()</code>메소드는 Object type을 리턴한다. 따라서 이를 형변환해주기 위해 2번째 파라미터로 UserDao.class를 넣어주는 것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[스프링] 2. 웹 백엔드 프로그래밍 기초 - JSTL & EL]]></title>
            <link>https://velog.io/@jmsuper_97/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%EC%8A%A4%ED%8A%B8%EC%BD%94%EC%8A%A4-%EC%9B%B9%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B3%BC%EC%A0%95-2.-%EC%9B%B9-%EB%B0%B1%EC%97%94%EB%93%9C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EA%B8%B0%EC%B4%88-JSTL-EL</link>
            <guid>https://velog.io/@jmsuper_97/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%EC%8A%A4%ED%8A%B8%EC%BD%94%EC%8A%A4-%EC%9B%B9%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B3%BC%EC%A0%95-2.-%EC%9B%B9-%EB%B0%B1%EC%97%94%EB%93%9C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EA%B8%B0%EC%B4%88-JSTL-EL</guid>
            <pubDate>Sat, 26 Nov 2022 08:50:24 GMT</pubDate>
            <description><![CDATA[<h2 id="jstljsp-standard-tag-library">JSTL(JSP Standard Tag Library)</h2>
<p>JSP 페이지에서 조건문 처리, 반복문 처리 등을 html tag형태로 작성할 수 있게 도와준다.초기에는 HTML코드와 JSP 스트립트릿 코드를 함께 썻다. 하지만, 프론트엔드 개발자 입장에서 유지보수가힘들다는 단점이 존재했다. 이런 문제를 해결하기 위해 등장한 것이 JSTL이다.</p>
<br>

<h3 id="jstl-설치방법">JSTL 설치방법</h3>
<ul>
<li>참고링크 : <a href="https://www.boostcourse.org/web326/lecture/258521?isDesc=false">https://www.boostcourse.org/web326/lecture/258521?isDesc=false</a></li>
<li>참고링크 : <a href="https://stackoverflow.com/questions/31043869/intellij-and-jsp-jstl-cannot-resolve-taglib-for-jstl-in-tomcat7">https://stackoverflow.com/questions/31043869/intellij-and-jsp-jstl-cannot-resolve-taglib-for-jstl-in-tomcat7</a></li>
<li>다운로드 링크 : <a href="http://tomcat.apache.org/download-taglibs.cgi">http://tomcat.apache.org/download-taglibs.cgi</a></li>
</ul>
<p>위 링크에서 Impl, Spec, EL jar파일들을 다운받고 WEB-INF폴더 하위의 lib폴더에 넣어줘야 한다.인텔리제이를 사용할 경우, 톰캣 lib폴더에 추가해주고, project structure에 가서 라이브러리를 추가해야 한다.</p>
<br>

<h3 id="jstl-코어태그--변수-지원-태그---set-remove">JSTL 코어태그 : 변수 지원 태그 - set, remove</h3>
<pre><code class="language-java">&lt;%@ page contentType=&quot;text/html;charset=UTF-8&quot; language=&quot;java&quot; %&gt;
&lt;%@ taglib uri=&quot;http://java.sun.com/jsp/jstl/core&quot; prefix=&quot;c&quot; %&gt;
&lt;c:set var=&quot;value1&quot; scope=&quot;request&quot; value=&quot;kang&quot;/&gt;
&lt;html&gt;
&lt;head&gt;
    &lt;title&gt;Title&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
성 : ${value1}&lt;br&gt;
&lt;c:remove var=&quot;value1&quot; scope=&quot;request&quot;/&gt;
성 : ${value1}&lt;br&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>taglib : jstl을 사용하기 위한 설정이다. 이때 uri값과 prefix를 지정해줘야 한다.set : 변수를 선언 및 초기화한다.remove : set되었던 변수를 제거한다.</p>
<br>

<h3 id="코어태그--변수-지원-태그---프로퍼티-맵의-처리-및-흐름제어-태그">코어태그 : 변수 지원 태그 - 프로퍼티, 맵의 처리 및 흐름제어 태그</h3>
<pre><code class="language-java">&lt;c:set target=&quot;${some}&quot; property=&quot;propertyName&quot; value=&quot;anyValue&quot;&gt;</code></pre>
<ul>
<li>some 객체가 자바빈일 경우 : some.setPropertyName(anyvalue)</li>
<li>some 객체가 맵(map)일 경우 : some.put(propertyName, anyValue)</li>
</ul>
<p>if의 경우 java의 if와는 다르게 else가 없다.</p>
<pre><code class="language-java">&lt;c:if test=&quot;조건&quot;&gt;
    조건이 true일 경우에 실행되는 코드 body
&lt;/c:if&gt;</code></pre>
<pre><code class="language-java">&lt;%@ page contentType=&quot;text/html;charset=UTF-8&quot; language=&quot;java&quot; %&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt;
&lt;c:set var=&quot;n&quot; value=&quot;0&quot; scope=&quot;request&quot;/&gt;
&lt;html&gt;
&lt;head&gt;
    &lt;title&gt;Title&lt;/title&gt;
&lt;/head&gt;
&lt;c:if test=&quot;${n == 0}&quot;&gt;
    n과 0은 같습니다.
&lt;/c:if&gt;
&lt;c:if test=&quot;${n == 10}&quot;&gt;
    n과 10은 같습니다.
&lt;/c:if&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<br>

<h3 id="코어-태그--흐름제어-태그---choose">코어 태그 : 흐름제어 태그 - choose</h3>
<pre><code class="language-java">&lt;c:choose&gt;
    &lt;c:when test=&quot;조건1&quot;&gt;
      ...
    &lt;/c:when&gt;
    &lt;c:when test=&quot;조건2&quot;&gt;
      ...
    &lt;/c:when&gt;
    &lt;c:otherwise&gt;
      ...
    &lt;/c:otherwise&gt;
&lt;/c:choose&gt;</code></pre>
<pre><code class="language-java">&lt;c:choose&gt;
    &lt;c:when test=&quot;${score &gt;= 90}&quot;&gt;
        A학생입니다.
    &lt;/c:when&gt;
    &lt;c:when test=&quot;${score &gt;= 80}&quot;&gt;
        B학생입니다.
    &lt;/c:when&gt;
    &lt;c:otherwise&gt;
        F학생입니다.
    &lt;/c:otherwise&gt;
&lt;/c:choose&gt;</code></pre>
<br>

<h3 id="코어-태그--흐름제어-태그---foreach">코어 태그 : 흐름제어 태그 - forEach</h3>
<p>forEach 태그는 반복문을 수행하는 태그이다.</p>
<ul>
<li>문법</li>
</ul>
<pre><code class="language-java">&lt;c:forEach var=&quot;변수&quot; items=&quot;아이템&quot; [begin=&quot;시작번호&quot;] [end=&quot;끝번호&quot;]&gt;
...
${변수}
...
&lt;/c:forEach&gt;</code></pre>
<ul>
<li>var : items 리스트의 각각의 원소들이 할당되는 변수</li>
<li>items : 반복가능한 Collection. 배열, list, Enumeration, Map</li>
<li>begin : items에 지정한 목록에서 값을 읽어올 인덱스의 시작값</li>
<li>end : items에 지정한 목록에서 값을 읽어올 인덱스의 끝값</li>
</ul>
<pre><code class="language-java">&lt;%@ page contentType=&quot;text/html;charset=UTF-8&quot; language=&quot;java&quot; %&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt;
&lt;%
    List&lt;String&gt; list = new ArrayList&lt;&gt;();
    list.add(&quot;hello&quot;);
    list.add(&quot;World&quot;);
    list.add(&quot;!!!!&quot;);

    request.setAttribute(&quot;list&quot;,list);
%&gt;
&lt;html&gt;
&lt;head&gt;
    &lt;title&gt;Title&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;c:forEach items=&quot;${list}&quot; var=&quot;item&quot; &gt;
    ${item} &lt;br&gt;
&lt;/c:forEach&gt;&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<br>

<h3 id="코어-태그--흐름제어태그---import">코어 태그 : 흐름제어태그 - import</h3>
<p>url로 부터 읽어온 결과를 지정한 변수에 저장한다.</p>
<ul>
<li>문법</li>
</ul>
<pre><code class="language-java">&lt;c:import url=&quot;URL&quot; charEncoding=&quot;캐릭터인코딩&quot; var=&quot;변수명&quot; scope=&quot;범위&quot;&gt;
    &lt;c:param name=&quot;파라미터이름&quot; value=&quot;파라미터값&quot;/&gt;
&lt;/c:import&gt;</code></pre>
<pre><code class="language-java">&lt;%@ page contentType=&quot;text/html;charset=UTF-8&quot; language=&quot;java&quot; %&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt;
&lt;c:import url=&quot;https://google.com&quot; var=&quot;urlValue&quot; scope=&quot;request&quot;/&gt;

&lt;html&gt;
&lt;head&gt;
    &lt;title&gt;Title&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    ${urlValue}
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<br>

<h3 id="코어-태그--흐름제어태그---redirect">코어 태그 : 흐름제어태그 - redirect</h3>
<p>지정한 페이지로 리다이렉트한다. Servlet에서 response.sendRedirect()를 수행하는 것과 유사하다.</p>
<ul>
<li>문법</li>
</ul>
<pre><code class="language-java">&lt;c:redirect url=&quot;리다이렉트할 URL&quot;&gt;
    &lt;c:param name=&quot;파라미터이름&quot; value=&quot;파라미터값&quot;&gt;
&lt;/c:redirect&gt;</code></pre>
<pre><code class="language-java">&lt;%@ page contentType=&quot;text/html;charset=UTF-8&quot; language=&quot;java&quot; %&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot;%&gt;
&lt;c:redirect url=&quot;http://localhost:8080/WebservletTest_war_exploded/jstl05.jsp&quot;/&gt;
&lt;html&gt;
&lt;head&gt;
    &lt;title&gt;Title&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;/body&gt;
&lt;/html&gt;</code></pre>
<br>

<h3 id="코어-태그-기타태그---out">코어 태그: 기타태그 - out</h3>
<p>JspWriter는 JSP페이지가 생성하는 결과를 출력할 때 사용되는 출력 스트림이다. out태그는 해당 스트림에 데이터를 출력해준다.</p>
<ul>
<li>문법</li>
</ul>
<pre><code class="language-java">&lt;c:out value=&quot;value&quot; escapeXml=&quot;{true|false}&quot; default=&quot;defalutValue&quot;/&gt;</code></pre>
<p>escapeXml : 이 속성의 값이 true일 경우 <code>&lt; , &amp; , &#39; , &quot;</code>와 같은 문자들을 변환해준다. 즉, javascript코드와 같은 코드들을 실행시키지 않고 코드 그 자체를 출력시켜준다.</p>
<pre><code class="language-java">&lt;%@ page contentType=&quot;text/html;charset=UTF-8&quot; language=&quot;java&quot; %&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt;
&lt;html&gt;
&lt;head&gt;
    &lt;title&gt;Title&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;c:set var=&quot;t&quot; value=&quot;&lt;script type=&#39;text/javascript&#39;&gt;alert(1);&lt;/script&gt;&quot;/&gt;
&lt;c:out value=&quot;${t}&quot; escapeXml=&quot;true&quot;/&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p><br><br></p>
<h2 id="elexperssion-language">EL(Experssion Language)</h2>
<ul>
<li>참고자료 : <a href="https://www.boostcourse.org/web326/lecture/258517?isDesc=false">https://www.boostcourse.org/web326/lecture/258517?isDesc=false</a></li>
</ul>
<p>EL은 값을 표현하는 데 사용되는 스크립트 언어로서 JSP의 기본 문법을 보완하는 역할을 한다.JSP에 자바 코드가 들어가면 프론트엔드 개발자가 봤을 때 당황할 수 있다. 이러한 점을 고려하여JSP에 있는 자바 코드를 이해하기 쉽게 표현하기 위한 EL이 등장했다.</p>
<br>

<h3 id="주요-기능">주요 기능</h3>
<ul>
<li>JSP의 스코프에 맞는 속성 사용. JSP의 내장객체보다 더 쉽게 스코프에 접근할 수 있다.</li>
<li>집합 객체에 대한 접근 방법 제공.</li>
<li>수치 연산, 관계 연산, 논리 연산자 제공</li>
<li>자바 클래스 메소드 호출 기능 제공</li>
<li>표현언어만의 기본 객체 제공</li>
</ul>
<br>

<h3 id="표현-방법">표현 방법</h3>
<ul>
<li>문법 <code>${expr}</code> : expr은 EL에서 정의한 문법에 따라 값을 표현하는 식이다.</li>
<li>예제 <code>&lt;b&gt;${sessionScope.member.id}&lt;/b&gt;</code></li>
</ul>
<br>

<h3 id="jsp-예제">JSP 예제</h3>
<pre><code class="language-java">&lt;%@ page contentType=&quot;text/html;charset=UTF-8&quot; language=&quot;java&quot; %&gt;
&lt;%
    pageContext.setAttribute(&quot;p1&quot;,&quot;page scope value&quot;);
    request.setAttribute(&quot;r1&quot;,&quot;request scope value&quot;);
    session.setAttribute(&quot;s1&quot;,&quot;session scope value&quot;);
    application.setAttribute(&quot;a1&quot;,&quot;application scope value&quot;);
%&gt;
&lt;html&gt;
&lt;head&gt;
    &lt;title&gt;Title&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
pageContext.getAttribute(&quot;p1&quot;) : &lt;%=pageContext.getAttribute(&quot;p1&quot;)%&gt;&lt;br&gt;

pageContext.getAttribute(&quot;p1&quot;) : ${pageScope.p1}&lt;br&gt;
request.getAttribute(&quot;r1&quot;) : ${requestScope.r1}&lt;br&gt;
session.getAttribute(&quot;s1&quot;) : ${sessionScope.s1}&lt;br&gt;
application.getAttribute(&quot;a1&quot;) : ${applicationScope.a1}&lt;br&gt;

pageContext.getAttribute(&quot;p1&quot;) : ${p1}&lt;br&gt;
request.getAttribute(&quot;r1&quot;) : ${r1}&lt;br&gt;
session.getAttribute(&quot;s1&quot;) : ${s1}&lt;br&gt;
application.getAttribute(&quot;a1&quot;) : ${a1}&lt;br&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[스프링] 2. 웹 백엔드 프로그래밍 기초 - JSP & redirect & forward & scope]]></title>
            <link>https://velog.io/@jmsuper_97/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%EC%8A%A4%ED%8A%B8%EC%BD%94%EC%8A%A4-%EC%9B%B9%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B3%BC%EC%A0%95-2.-%EC%9B%B9-%EB%B0%B1%EC%97%94%EB%93%9C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EA%B8%B0%EC%B4%88-Request-Response-%EA%B0%9D%EC%B2%B4-%EC%9D%B4%ED%95%B4</link>
            <guid>https://velog.io/@jmsuper_97/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%EC%8A%A4%ED%8A%B8%EC%BD%94%EC%8A%A4-%EC%9B%B9%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B3%BC%EC%A0%95-2.-%EC%9B%B9-%EB%B0%B1%EC%97%94%EB%93%9C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EA%B8%B0%EC%B4%88-Request-Response-%EA%B0%9D%EC%B2%B4-%EC%9D%B4%ED%95%B4</guid>
            <pubDate>Sat, 26 Nov 2022 04:02:30 GMT</pubDate>
            <description><![CDATA[<h2 id="jspjavaserver-pages">JSP(JavaServer Pages)</h2>
<h3 id="jsp란">JSP란?</h3>
<ul>
<li>서블릿을 통해 HTML을 문서를 작성할 수 있지만, print()안에 일일이 넣어야 한다는단점이 존재했다.</li>
<li>이를 해결하기 위해 HTML형식으로 작성할 수 있는 JSP가 등장하였다.</li>
<li>JSP는 실제 서블릿 기술을 사용한다.</li>
<li>즉, HTML과 비슷하게 생겼지만, HTML처럼 실행되는 것이 아니라 서블릿으로 봐뀐 이후에 서블릿 작동 방식으로 실행된다. 따라서 Lifecycle 또한 서블릿과 동일하다.</li>
</ul>
<p>EX)</p>
<pre><code class="language-java">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
    pageEncoding=&quot;UTF-8&quot;%&gt;
&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;

&lt;%
    int total = 0;
    for(int i = 1; i &lt;= 10; i++){
        total = total + i;
    }
%&gt;

1부터 10까지의 합 : &lt;%=total %&gt;

&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>Java코드는 &lt;% %&gt; 안에 집어넣는다. 그리고 &lt;% %&gt;안에 있던 코드들의 값들을 HTML에 포함시켜 전송하기 위해서는 &lt;%=변수명 %&gt;을 사용한다. &lt;%=변수명 %&gt;은 println(변수명)과 동일한 의미이다.</p>
<br>

<h3 id="jsp-라이프싸이클">JSP 라이프싸이클</h3>
<p>WAS는 웹 브라우저로부터 JSP에 대한 요청을 받게 되면, JSP코드를 서블릿 소스코드로 변환한 후 컴파일하여 실행되게 된다. 서블릿으로 컴파일되어 실행될 때 상황에 따라서 어떤 메소드들이 실행되는지 잘알아야, JSP를 알맞게 작성할 수 있다.JSP파일은 컴파일 시 Servlet코드로 변환된다. 변환된 파일은 아래 경로를 통해 찾을 수 있다.</p>
<p><code>.metadata</code> -&gt; <code>.plugins</code> -&gt; <code>org.eclipse.wst.server.core</code> -&gt; <code>tmp0</code> -&gt; <code>work</code> -&gt; <code>Catalina</code>-&gt; <code>localhost</code> -&gt; <code>프로젝트명</code> -&gt; <code>org</code> -&gt; <code>apache</code> -&gt; <code>jsp</code>경로에 들어가면 class파일과 java파일을 확인할 수 있다.</p>
<p>java파일</p>
<pre><code class="language-java">      out.write(&quot;\r\n&quot;);
      out.write(&quot;&lt;!DOCTYPE html&gt;\r\n&quot;);
      out.write(&quot;&lt;html&gt;\r\n&quot;);
      out.write(&quot;&lt;head&gt;\r\n&quot;);
      out.write(&quot;&lt;meta charset=\&quot;UTF-8\&quot;&gt;\r\n&quot;);
      out.write(&quot;&lt;title&gt;Insert title here&lt;/title&gt;\r\n&quot;);
      out.write(&quot;&lt;/head&gt;\r\n&quot;);
      out.write(&quot;&lt;body&gt;\r\n&quot;);
      out.write(&quot;\r\n&quot;);

    int total = 0;
    for(int i = 1; i &lt;= 10; i++){
        total = total + i;
    }

      out.write(&quot;\r\n&quot;);
      out.write(&quot;\r\n&quot;);
      out.write(&quot;1부터 10까지의 합 : &quot;);
      out.print(total );
      out.write(&quot;\r\n&quot;);
      out.write(&quot;\r\n&quot;);
      out.write(&quot;&lt;/body&gt;\r\n&quot;);
      out.write(&quot;&lt;/html&gt;&quot;);</code></pre>
<p>HTML문서의 각 줄을 out.writer()를 통해 변환된 것을 알 수 있다.</p>
<br>

<h3 id="sum10jsp가-실행될-때-벌어지는-일">sum10.jsp가 실행될 때 벌어지는 일</h3>
<ul>
<li>이클립스 워크스페이스 아래의 .metadata 폴더에 sum10_jsp.java파일이 생성된다.</li>
<li>해당 파일의 &quot;jspService()&quot;메소드 안을 살펴 보면 jsp파일의 내용이 변환되서 들어가있는 것을 확인할 수 있다.</li>
<li>sum10_jsp.java는 서블릿 소스로 자동으로 컴파일 되면서 실행되어 그 결과가 브라우저에 보여진다.</li>
</ul>
<br>

<h3 id="jsp의-실행순서">JSP의 실행순서</h3>
<ol>
<li>브라우저가 웹서버에 JSP에 대한 요청 정보를 전달한다.</li>
<li>브라우저가 요청한 JSP가 최초로 요청했을 경우만</li>
<li>JSP로 작성된 코드가 서블릿 코드로 변환된다. (java 파일 생성)</li>
<li>서블릿 코드를 컴파일해서 실행가능한 bytecode로 변환한다. (class 파일 생성)</li>
<li>서블릿 클래스를 로딩하고 인스턴스를 생성한다.</li>
<li>서블릿이 실행되어 요청을 처리하고 응답 정보를 생성한다.</li>
</ol>
<br>

<h3 id="스크립트-요소의-이해">스크립트 요소의 이해</h3>
<ul>
<li>JSP페이지에서는 선언문, 스크립트릿, 표현식 이라는 3가지의 스크립트 요소를 제공한다.</li>
<li>선언문(Declaration) - &lt;%! %&gt; : 전역변수 선언 및 메소드 선언에 사용</li>
<li>스크립트릿(Scriptlet) - &lt;% %&gt; : 프로그래밍 코드 기술에 사용</li>
<li>표현식(Expression) - &lt;%=%&gt; : 화면에 출력할 내용 기술에 사용</li>
</ul>
<br>

<h3 id="스크립트릿">스크립트릿</h3>
<pre><code class="language-java">&lt;%
for(int i = 1; i &lt;= 5; i++){
%&gt;
&lt;H&lt;%=i %&gt;&gt; 아름다운 한글 &lt;/H&lt;%=i %&gt;&gt;
&lt;%
}
%&gt;</code></pre>
<p>위와 같은 방식을 수행했을 때, for문 안에있는 HTML코드가 반복되어 실행된다.</p>
<br>

<h3 id="주석">주석</h3>
<ul>
<li>사용가능한 주석 : HTML 주석, Java 주석, JSP 주석</li>
<li>JSP 주석은 JSP에서 Java코드로 변환될 때 주석부분은 변환되지 않는다.</li>
<li>Java 주석은 JSP에서 Java코드로 변환될 떄는 함께 변환되지만,Java에서 HTML으로 변환될 때 Java주석부분은 변환되지 않는다.</li>
<li>HTML주석은 함께 변환되지만, 브라우저에서는 주석으로 표시되어 화면에 나타나지 않는다.</li>
</ul>
<br>

<h3 id="jsp-내장객체란">JSP 내장객체란</h3>
<ul>
<li>JSP를 실행하면 서블릿 소스가 생성되고 실행된다.</li>
<li>JSP에 입력한 대부분의 코드는 생성되는 서블릿 소스의 _ jspService() 메소드 안에 삽입되는 코드로 생성된다.</li>
<li>_ jspService()에 삽입된 코드의 윗 부분에 미리 선언된 객체들이 있는데, 해당 객체들은 jsp에서도 사용가능하다.</li>
<li>response, request, application, session, out과 같은 변수를 내장객체라고 한다.</li>
</ul>
<pre><code class="language-java">      response.setContentType(&quot;text/html; charset=UTF-8&quot;);
      pageContext = _jspxFactory.getPageContext(this, request, response,
                  null, true, 8192, true);
      _jspx_page_context = pageContext;
      application = pageContext.getServletContext();
      config = pageContext.getServletConfig();
      session = pageContext.getSession();
      out = pageContext.getOut();
      _jspx_out = out;</code></pre>
<br>

<h3 id="내장-객체의-종류">내장 객체의 종류</h3>
<ul>
<li>request : HTML Form 요소 선택 값과 같은 사용자 입력 정보를 읽어올 때 사용</li>
<li>response : 사용자 요청에 대한 응답을 처리할 때 사용</li>
<li>pageContext : 현재 JSP 실행에 대한 context 정보를 참조하기 위해 사용</li>
<li>session : 클라이언트 세션 정보를 처리하기 위해 사용</li>
<li>application : 웹 서버의 어플리케이션 처리와 관련된 정보를 참조하기 위해 사용</li>
<li>out : 사용자에게 전달하기 위한 output 스트림을 처리하기 위해 사용</li>
<li>config : 현재 JSP에 대한 초기화 환경을 처리하기 위해 사용</li>
<li>page : 현재 JSP 페이지에 대한 클래스 정보</li>
<li>exception : 예외 처리를 위해 사용</li>
</ul>
<p><br><br></p>
<h2 id="redirect--forward">redirect &amp; forward</h2>
<h3 id="redirect">redirect</h3>
<ul>
<li>리다이렉트는 http프로토콜로 정해진 규칙이다.</li>
<li>서버는 클라이언트로부터 요청을 받은 후, 클라이언트에게 특정 URL로 이동하라고 요청할 수 있다.이를 리다이렉트라고 한다.</li>
<li>서버에서는 클라이언트에게 응답으로 상태코드를 302와 함께 이동할 URL정보를 Location 헤더에 담아전송한다. 클라이언트는 서버로 부터 받은 상태값이 302이면 Location헤더값으로 재요청을 보내게 된다.이때 브라우저의 주소창은 전송받은 URL로 바뀌게 된다.</li>
<li>서블릿이나 jsp는 redirect하기 위해서 HttpServletResponse가 가지고 있는 sendRedirect()메소드를사용한다.</li>
<li>리다이렉트하면 클라이언트가 요청을 2번하게 된다.</li>
<li>처음 요청에 의해 생긴 객체와 리다이렉트 이후 생긴 객체는 서로 다른 객체이다.</li>
</ul>
<p>EX)</p>
<pre><code class="language-java">response.sendRedirect(&quot;redirect02.jsp&quot;);</code></pre>
<br>

<h3 id="redirect-동작-설명">redirect 동작 설명</h3>
<p><img src="https://github.com/JMsuper/boostcourse_web_backend/raw/main/img/redirect%20process.png" alt="https://github.com/JMsuper/boostcourse_web_backend/raw/main/img/redirect%20process.png"></p>
<br>

<h3 id="forward">forward</h3>
<p><img src="https://github.com/JMsuper/boostcourse_web_backend/raw/main/img/forward%20process.png" alt="https://github.com/JMsuper/boostcourse_web_backend/raw/main/img/forward%20process.png"></p>
<ol>
<li>웹 브라우저에서 Servlet1에게 요청을 보낸다.</li>
<li>Servlet1은 요청을 처리한 후, 그 결과를 HttpServletRequest에 저장한다.HttpServletRequest 객체는 요청 과정동안 살아있기 때문에 해당 객체에 저장하는 것이다.</li>
<li>Servlet1은 결과과 저장된 HttpServletRequest와 응답을 위한 HttpServletResponse를 같은 웹어플리케이션 안에 있는 Servlet2에게 전송한다.(forward)Servlet2는 HttpServletRequest,Response 객체의 래퍼런스를 모르기 때문에 알려줘야 한다.</li>
<li>Servlet2는 Servlet1으로 부터 받은 HttpServletRequest와 HttpServletResponse를 이용하여요청을 처리한 후 웹 브라우저에게 결과를 전송한다.</li>
</ol>
<p>EX) </p>
<p>Servlet1.java</p>
<pre><code class="language-java">    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        int diceValue = (int)(Math.random() * 6) + 1;
        request.setAttribute(&quot;dice&quot;, diceValue);

        RequestDispatcher requestDispatcher = request.getRequestDispatcher(&quot;/next&quot;);
        requestDispatcher.forward(request, response);
    }</code></pre>
<p><code>request.setAttribute(&quot;dice&quot;, diceValue);</code> : request 객체에 &quot;dice&quot;라는 속성값으로 diveValue를 저장한다.이때 diveValue는 Object로 저장된다. 왜냐하면 다양한 type의 데이터가 저장될 수 있기 때문이다.</p>
<p><code>RequestDispatcher requestDispatcher = request.getRequestDispatcher(&quot;/next&quot;);</code> :</p>
<p> 파라미터는 forward할 servlet의 경로이다.파라미터 값에 &quot;/&quot;를 가장 앞단에 두면 relative path로 작동한다. 본 servlet이 위치하던 폴더를 기준으로 servlet을 검색할 것이다.RequstDispathcer 클래스는 현재 request에 담긴 정보를 저장하고 있다가 그 다음 페이지에도 해당 정보를 볼 수 있게하는 클래스이다.</p>
<p><code>requestDispatcher.forward(request, response);</code> : request, response객체를 resquestDispatcher가 가지고 있는 경로의 servlet으로 이동한다.
<br>
Servlet2.java</p>
<pre><code class="language-java">    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType(&quot;text/html&quot;);
        PrintWriter out = response.getWriter();
        out.println(&quot;&lt;html&gt;&quot;);
        out.println(&quot;&lt;head&gt;&lt;title&gt;form&lt;/title&gt;&lt;/head&gt;&quot;);
        out.println(&quot;&lt;body&gt;&quot;);

        // getAttribute는 Object를 리턴하기 때문에 
        // Interger로 형변환해줘야 한다.
        int dice = (Integer)request.getAttribute(&quot;dice&quot;);
        out.println(&quot;dice: &quot; + dice + &quot;&lt;br&gt;&quot;);
        for(int i = 0; i &lt; dice; i++) {
            out.print(&quot;hello&lt;br&gt;&quot;);
        }
        out.println(&quot;&lt;/body&gt;&quot;);
        out.println(&quot;&lt;/html&gt;&quot;);
    }</code></pre>
<p><code>int dice = (Integer)request.getAttribute(&quot;dice&quot;);</code> : request에 저장된 attribute는 기본적으로 Object type이다.따라서 이를 알맞은 type으로 형변환해줘야 한다.</p>
<br>

<h3 id="servlet과-jsp연동">Servlet과 JSP연동</h3>
<ul>
<li>Servlet은 프로그램 로직이 수행되기에 유리하다. IDE 등에서 지원을 좀 더 잘해준다.</li>
<li>JSP는 결과를 출력하기에 Servlet보다 유리하다. 필요한 html문을 그냥 입력하게 된다.</li>
<li>프로그램 로직 수행은 Servlet에서, 결과 출력은 JSP에서 하는 것이 유리하다.</li>
<li>Servlet과 JSP의 장단점을 해결하기 위해서 Servlet에서 프로그램 로직이 수행되고, 그 결과를</li>
<li>JSP에게 포워딩하는 방법이 사용되게 되었다. 이를 Servlet과 JSP연동이라고 한다.</li>
</ul>
<p>EX)</p>
<pre><code class="language-java">&lt;%
    int v1 = (int)request.getAttribute(&quot;v1&quot;);
    int v2 = (int)request.getAttribute(&quot;v2&quot;);
    int result = (int)request.getAttribute(&quot;result&quot;);
%&gt;
&lt;%=v1 %&gt; + &lt;%=v2 %&gt; = &lt;%=result %&gt;</code></pre>
<p>forward()를 실행하는 Servlet에서의 코드는 서블릿과 서블릿과의 forward()와 동일하다.다만, JSP의 경우 forward()를 받는 서블릿의 코드를 JSP문법에 맞게 작성하면된다.이때 request는 JSP코드가 서블릿코드로 변환되면서 초기화되는 내장객체이기 때문에 사용가능하다.</p>
<p><br><br></p>
<h2 id="scope">scope</h2>
<h3 id="4가지의-scope">4가지의 scope</h3>
<ul>
<li><p>Application : 웹 어플리케이션이 시작되고 종료될 때까지 변수가 유지되는 경우 사용</p>
</li>
<li><p>Session : 웹 브라우저 별로 변수가 관리되는 경우 사용</p>
</li>
<li><p>Request : http요청을 WAS가 받아서 웹 브라우저에게 응답할 때까지 변수가 유지되는 경우 사용</p>
</li>
<li><p>Page : 페이지 내에서 지역변수처럼 사용</p>
<p>  <img src="https://github.com/JMsuper/boostcourse_web_backend/raw/main/img/JSP%20scope.png" alt="https://github.com/JMsuper/boostcourse_web_backend/raw/main/img/JSP%20scope.png"></p>
</li>
</ul>
<br>

<h3 id="page-scope">Page scope</h3>
<ul>
<li>PageContext 추상 클래스를 사용한다.</li>
<li>JSP 페이지에서 pageContext 라는 내장 객체로 사용가능 하다.</li>
<li>forward가 될 경우 해당 Page scope에 지정된 변수는 사용할 수 없다.</li>
<li>사용방법은 다른 scope들과 동일하다</li>
<li>마치 지역변수처럼 사용된다는 것이 다른 Scope들과 다르다.</li>
<li>JSP에서 pageScope에 값을 저장 한 후 해당 값을 EL표기법에서 사용할 때 사용된다.지역 변수처럼 해당 JSP나 서블릿이 실행되는 동안에만 정보를 유지하고자 할 때 사용된다.</li>
<li>pageContext는 실제 사용할 일이 많이 없고, JSP에서 서블릿으로 변환될 때 내장 객체 초기화에이용된다.</li>
</ul>
<p>JSP를 서블릿으로 변환한 코드 중 일부</p>
<pre><code class="language-java">pageContext = _jspxFactory.getPageContext(this, request, response,
                  null, true, 8192, true);
      _jspx_page_context = pageContext;
      application = pageContext.getServletContext();
      config = pageContext.getServletConfig();
      session = pageContext.getSession();
      out = pageContext.getOut();</code></pre>
<br>

<h3 id="request-scope">request scope</h3>
<ul>
<li>http요청을 WAS가 받아서 웹 브라우저에게 응답할 때까지 변수값을 유지하고자 할 경우</li>
<li>HttpServletRequest객체를 사용한다.</li>
<li>JSP에서는 request내장 변수를 사용한다.</li>
<li>서블릿에서는 HttpServletRequest객체를 사용한다.</li>
<li>값을 저장할 때는 request객체의 setAttribute()메소드를 사용한다.</li>
<li>값을 읽어들일 때는 request객체의 getAttribute()메소드를 사용한다.</li>
<li>forward시 값을 유지하고자 사용한다.</li>
<li>앞에서 forward에 대하여 배울 때 forward하기 전에 request객체의 setAttribute()메소드로 값을설정한 후, 서블릿이나 jsp에게 결과를 전달하여 값을 출력하도록 하였는데 이렇게 포워드 되는동안 값이 유지되는 것이 Request scope를 이용했다고 한다.</li>
</ul>
<p>forward()를 수행하는 서블릿</p>
<pre><code class="language-java">request.setAttribute(&quot;v1&quot;, v1);
...
requestDispatcher.forward(request,response);</code></pre>
<p>forward() 당하는 서블릿</p>
<pre><code class="language-java">int v1 = (int)request.getAttribute(&quot;v1&quot;);</code></pre>
<br>

<h3 id="session-scope">Session scope</h3>
<ul>
<li>웹 브라우저 별로 변수를 관리하고자 할 경우 사용한다.</li>
<li>웹 브라우저간의 탭간에는 세션정보가 공유되기 때문에, 각각의 탭에서는 같은 세션정보를사용할 수 있다.</li>
<li>HttpSession인터페이스를 구현한 객체를 사용한다.</li>
<li>JSP에서는 session내장 변수를 사용한다.</li>
<li>서블릿에서는 HttpServletRequest의 getSession()메소드를 이용하여 session객체를 얻는다.</li>
<li>값을 저장할 때는 session 객체의 setAttribute()메소드를 사용한다.</li>
<li>값을 읽어들일 때는 session 객체의 getAttribute()메소드를 사용한다.</li>
<li>장바구니처럼 사용자별로 유지가 되어야 할 정보가 있을 때 사용한다.</li>
</ul>
<br>

<h3 id="application-scope">Application scope</h3>
<ul>
<li>웹 어플리케이션이 시작되고 종료될 때까지 변수를 사용할 수 있다.</li>
<li>ServletContext 인터페이스를 구현한 객체를 사용한다.</li>
<li>jsp에서는 application 내장 객체를 이용한다.</li>
<li>서블릿의 경우는 getServletContext()메소드를 이용하여 application 객체를 이용한다.</li>
<li>웹 어플리케이션 하나당 하나의 application객체가 사용된다.</li>
<li>값을 저장할 때는 application 객체의 setAttribute()메소드를 사용한다.</li>
<li>값을 읽어들일 때는 application 객체의 getAttribute()메소드를 사용한다.</li>
<li>모든 클라이언트가 공통으로 사용해야할 값들이 있을 때 사용한다.</li>
</ul>
<p>Servlet1</p>
<pre><code class="language-java">    ServletContext application = getServletContext();
        int value = 1;
        application.setAttribute(&quot;value&quot;, value);</code></pre>
<p>Servlet2</p>
<pre><code class="language-java">    ServletContext application = getServletContext();
    int value = (int)application.getAttribute(&quot;value&quot;);
    value++;
    application.setAttribute(&quot;value&quot;, value);</code></pre>
<p>위에서 application 객체에 집어넣은 value값은 다른 클라이언트에서 접속할 때도 공유되는 값이다.만약, getAttribute(공유자원이름)에서 공유자원이름에 해당하는 것이 없을 경우NullPointerException을 발생시킨다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[스프링] 2. 웹 백엔드 프로그래밍 기초 - 서블릿 및 서블릿 작성방법]]></title>
            <link>https://velog.io/@jmsuper_97/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%EC%8A%A4%ED%8A%B8%EC%BD%94%EC%8A%A4-%EC%9B%B9%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B3%BC%EC%A0%95-2.-%EC%9B%B9-%EB%B0%B1%EC%97%94%EB%93%9C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EA%B8%B0%EC%B4%88-%EC%84%9C%EB%B8%94%EB%A6%BF-%EB%B0%8F-%EC%84%9C%EB%B8%94%EB%A6%BF-%EC%9E%91%EC%84%B1%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@jmsuper_97/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%EC%8A%A4%ED%8A%B8%EC%BD%94%EC%8A%A4-%EC%9B%B9%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B3%BC%EC%A0%95-2.-%EC%9B%B9-%EB%B0%B1%EC%97%94%EB%93%9C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EA%B8%B0%EC%B4%88-%EC%84%9C%EB%B8%94%EB%A6%BF-%EB%B0%8F-%EC%84%9C%EB%B8%94%EB%A6%BF-%EC%9E%91%EC%84%B1%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Fri, 25 Nov 2022 08:54:27 GMT</pubDate>
            <description><![CDATA[<h4 id="학습목표">학습목표</h4>
<ul>
<li>자바 웹 어플리케이션의 구조를 이해한다.</li>
<li>서브릿에 대하여 이해한다.</li>
</ul>
<br>

<h3 id="자바-웹-어플리케이션java-web-application">자바 웹 어플리케이션(Java Web Application)</h3>
<ul>
<li>WAS에 설치되어 동작하는 어플리케이션</li>
<li>자바 웹 어플리케이션에는 HTML,CSS,이미지,자바로 작성된 클래스, 각종 설정 파일 등이 포함된다.</li>
<li>블로그, 쇼핑몰과 같은 것들이 web app이다.</li>
</ul>
<br>

<h3 id="자바-웹-어플리케이션의-폴더-구조">자바 웹 어플리케이션의 폴더 구조</h3>
<p><img src="https://github.com/JMsuper/boostcourse_web_backend/raw/main/img/%EC%9E%90%EB%B0%94%20%EC%9B%B9%20%EC%96%B4%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%20%ED%8F%B4%EB%8D%94%20%EA%B5%AC%EC%A1%B0.png" alt="https://github.com/JMsuper/boostcourse_web_backend/raw/main/img/%EC%9E%90%EB%B0%94%20%EC%9B%B9%20%EC%96%B4%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%20%ED%8F%B4%EB%8D%94%20%EA%B5%AC%EC%A1%B0.png"></p>
<ul>
<li>WEB-INF 폴더와 web.xml파일이 중요하다.</li>
<li>web.xml파일은 웹 어플리케이션에 관한 정보를 다 가지고 있는 파일이다.(서블릿 버전을 포함한다.)</li>
<li>WEB-INF 폴더 하위 폴더인 lib 폴더에는 각종 jar파일 형태의 라이브러리 파일들이 들어간다.</li>
<li>작성한 servlet 파일들은 classes 폴더안에 들어간다.</li>
</ul>
<br>

<h3 id="이클립스에서-실행된-dynamic-web-project">이클립스에서 실행된 Dynamic Web Project</h3>
<ul>
<li><p>이클립스에서 Dynamic Web Project의 Servlet을 실행하면, 해당 프로젝트가 이클립스가 관리하는 .metadata 폴더아래에 자바 웹 어플리케이션 폴더 구조로 만들어져 실행된다.</p>
</li>
<li><p>경로 <code>elipse-workspace</code> -&gt; <code>.metadata</code> -&gt; <code>.plugins</code> -&gt; <code>org.elipse.wst.server.core</code>&gt; <code>tmp0</code> -&gt; <code>wtpwebapps</code>실제 폴더에서 web.xml파일은 ROOT 폴더 하위 폴더에 들어가있다.</p>
<p>  <img src="https://github.com/JMsuper/boostcourse_web_backend/raw/main/img/%EC%9E%90%EB%B0%94%20%EC%9B%B9%20%ED%8F%B4%EB%8D%94.png" alt="https://github.com/JMsuper/boostcourse_web_backend/raw/main/img/%EC%9E%90%EB%B0%94%20%EC%9B%B9%20%ED%8F%B4%EB%8D%94.png"></p>
</li>
<li><p>web.xml 파일</p>
<pre><code class="language-xml">  &lt;?xml version=&quot;1.0&quot; encoding=&quot;ISO-8859-1&quot;?&gt;
  &lt;web-app version=&quot;2.5&quot; xsi:schemaLocation=&quot;http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd&quot;
  xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xmlns=&quot;http://java.sun.com/xml/ns/javaee&quot;&gt; &lt;/web-app&gt;</code></pre>
</li>
</ul>
<br>

<h3 id="servlet이란">Servlet이란?</h3>
<ul>
<li>자바 웹 어플리케이션의 구성요소 중 동적인 처리를 하는 프로그램</li>
<li>서블릿을 정의 해보면<ul>
<li>서블릿(servlet)은 WAS에서 동작하는 Java 클래스이다.</li>
<li>서블릿은 HttpServlet 클래스를 상속받아야 한다.</li>
<li>서블릿만으로 HTML을 표현하기는 무리가 있다.</li>
<li>서블릿과 JSP로부터 최상의 결과를 얻으려면, 웹 페이지를 개발할 때 이 두 가지를조화롭게 사용해야 한다. 예 : 웹 페이지를 구성하는 화면(HTML)은 JSP로 표현하고, 복잡한 프로그래밍은 서블릿으로 구현한다.</li>
</ul>
</li>
</ul>
<h3 id="servlet의-탄생배경">Servlet의 탄생배경</h3>
<p>먼저, CGI(Common Gateway Interface)에 대해서 알아야 한다.
웹의 역사에서 동적 페이지를 필요해짐에 따라, 서버는 클라이언트의 요청에 응답하여 처리하는 프로그램이 필요해졌다. 즉, 미리 저장해두었던 정적 페이지가 아닌 요구사항에 맞춰 만들어진 동적 페이지를 응답해줘야 하는 상황에 직면했다.
이를 해결하기 위해 등장한 것이 CGI이다. CGI란 웹 서버와 외부 프로그램 사이에 정보를 주고받는 일종의 규약을 말한다. CGI의 의미는 공통 게이트웨이 인터페이스인데, 이를 풀어서 생각해보자.</p>
<p><code>공통</code> : 너도 나도 포함된다.
<code>게이트웨이</code> : 연결 다리.
<code>인터페이스</code> : 상호간의 연결을 쉽게 해주는 것.</p>
<p>그런데 <strong>CGI는 프로세스 단위로 실행된다는 단점</strong>을 가지고 있었다. 웹 서버에 요청이 많아지면 그 만큼 실행되는 프로세스가 많아져 서버에 부하가 발생한다. 이를 보완하기 위해 프로세스가 아닌 <strong>스레드 단위로 동작하는 프로그램</strong>이 등장하게 된다. <strong>그것이 바로 Servlet이다.</strong></p>
<p><img src="https://github.com/JMsuper/boostcourse_web_backend/raw/main/img/CGI%20%ED%94%8C%EB%A1%9C%EC%9A%B0.png" alt="https://github.com/JMsuper/boostcourse_web_backend/raw/main/img/CGI%20%ED%94%8C%EB%A1%9C%EC%9A%B0.png"></p>
<br>

<h3 id="servlet과-cgi의-차이점">Servlet과 CGI의 차이점</h3>
<p>참고링크 : <a href="https://www.geeksforgeeks.org/difference-between-java-servlet-and-cgi/">https://www.geeksforgeeks.org/difference-between-java-servlet-and-cgi/</a></p>
<table>
<thead>
<tr>
<th>Basis</th>
<th>Servlet</th>
<th>CGI</th>
</tr>
</thead>
<tbody><tr>
<td>Approach</td>
<td>스레드 기반</td>
<td>프로세스 기반</td>
</tr>
<tr>
<td>사용 언어</td>
<td>JAVA</td>
<td>상관없음</td>
</tr>
<tr>
<td>OOP</td>
<td>O</td>
<td>X</td>
</tr>
<tr>
<td>Portability</td>
<td>O</td>
<td>X</td>
</tr>
<tr>
<td>Persistence</td>
<td>명시적으로 해제될 때까지 메모리에 유지</td>
<td>요청 종료시 해제</td>
</tr>
<tr>
<td>Server Indepent</td>
<td>O</td>
<td>X</td>
</tr>
<tr>
<td>Data Sharing</td>
<td>O</td>
<td>X</td>
</tr>
</tbody></table>
<br>

<h3 id="request-response-객체-이해">Request, Response 객체 이해</h3>
<h4 id="요청과-응답">요청과 응답</h4>
<p>WAS는 웹 브라우저로부터 Servlet요청을 받으면,
요청할 때 가지고 있는 정보를 HttpServletRequest객체를 생성하여 저장
웹 브라우저에게 응답을 보낼 때 사용하기 위하여 HttpServletResponse객체를 생성
생성된 HttpServletRequest, HttpServletResponse 객체를 서블릿에게 전달
추상화를 통해 http프로토콜에 맞춰 직접 구현하지 않아도 된다.</p>
<h4 id="httpservletrequest">HttpServletRequest</h4>
<p>http프로토콜의 request정보를 서블릿에게 전달하기 위한 목적으로 사용
헤더정보, 파라미터, 쿠키, URI, URL 등의 정보를 읽어 들이는 메소드를 가지고 있다.
Body의 Stream을 읽어 들이는 메소드를 가지고 있다.</p>
<h4 id="httpservletresponse">HttpServletResponse</h4>
<p>WAS는 어떤 클라이언트가 요청을 보냈는지 알고 있고, 해당 클라이언트에게 응답을 보내기 위한 HttpServletResponse객체를 생성하여 서블릿에게 전달
WAS는 클라이언트의 주소를 알고 있어, 개발자는 Response객체에 정보만 담아두면 WAS가 클라이언트에게 전달해준다.
서블릿은 해당 객체를 이용하여 content type, 응답코드, 응답 메시지등을 전송</p>
<p><br><br></p>
<h2 id="servlet-작성-방법">Servlet 작성 방법</h2>
<p>실제 개발에서 Servlet을 직접 사용하지는 않는다. 프레임워크들이 더 쉽게 구현할 수 있도록 도와주기 때문에 프레임워크를 쓴다. 그럼에도 불구하고, Servlet이 없이는 프레임워크들도 동작할 수 없기 때문에, Servlet을 이해하는 것은 웹의 동작을 이해하는데 도움이 된다.</p>
<h3 id="학습-목표">학습 목표</h3>
<ul>
<li>서블릿을 작성할 수 있다.</li>
<li>서블릿 버전에 따른 web.xml을 적절하게 작성할 수 있다.</li>
</ul>
<h3 id="servlet-작성방법은-2가지로-나뉨">Servlet 작성방법은 2가지로 나뉨</h3>
<ol>
<li>Servlet 3.0 spec 이상에서 사용하는 방법<ul>
<li>web.xml파일을 사용하지 않음.</li>
<li>자바 어노테이션(annotation)을 사용</li>
</ul>
</li>
<li>Servlet 3.0 spec 미만에서 사용하는 방법<ul>
<li>Servlet을 등록할 때 web.xml파일에 등록</li>
</ul>
</li>
</ol>
<h3 id="servlet-30-spec-이상">Servlet 3.0 spec 이상</h3>
<pre><code class="language-java">@WebServlet(&quot;/ttt&quot;)
public class TenServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    /**
     * @see HttpServlet#HttpServlet()
     */
    public TenServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType(&quot;text/html;charset=utf-8;&quot;);
        PrintWriter out = response.getWriter();
        out.print(&quot;&lt;h1&gt;1-10까지 출력!!&lt;/h1&gt;&quot;);
        for(int i = 1; i &lt;= 10; i++) {
            out.print(i+&quot;&lt;br&gt;&quot;);
        }
        out.close(); 
    }
}</code></pre>
<p>class 위에 있는 <code>@WebServlet(&quot;/ttt&quot;)</code>문구가 annotation이다. 이를 통해 <code>web.xml</code> 파일없이 &quot;/ttt&quot; 요청이들어왔을 때, 해당 클래스의 메소드를 이용하도록 만들 수 있다.</p>
<p><code>response.setContentType(&quot;text/html;charset=utf-8;&quot;);</code> : 응답 객체의 ContentType을 지정한다. 클라이언트가 서버로 부터 데이터를 전송받을 때, 해당 데이터가어떤 타입의 데이터인지 알아야지 이를 parsing할 수 있다.</p>
<p><code>PrintWriter out = response.getWriter();</code> : HttpServletResponse 객체의 메소드인 getWriter()함수는 PrintWriter 객체를 리턴한다.PrintWriter 객체를 통해 response body에 담길 데이터를 입력할 수 있다.</p>
<h4 id="serialversionuid-정체가-뭔가">serialVersionUID? 정체가 뭔가?</h4>
<p><code>private static final long serialVersionUID = 1L;</code>
HttpServlet은 <code>Serializable</code> 인터페이스를 <code>implements</code> 한다. IDE에서는 <code>Serializable</code> 인터페이스의 구현체에 대해 위 변수를 할당하는 것을 추천한다. 그렇다면 왜 그럴까?
같은 클래스라도 위 직렬화ID에 따라 구분된다. 명시적으로 위 직렬화ID를 할당하지 않으면, 컴파일러에서 임의로 할당한다. 그런데, 직렬화ID가 수정될 경우, 역직렬화 과정에서 에러가 발생할 수 있다고 한다. 이러한 이유로 직렬화ID를 명시하는 것을 추천한다.
<em>(더 자세한 내용은 따로 &#39;Java 직렬화&#39;라는 포스팅을 해야겠다.)</em></p>
<br>

<h3 id="servlet-30-미만">servlet 3.0 미만</h3>
<p>web.xml 파일 중 일부</p>
<pre><code class="language-xml">  &lt;servlet&gt;
    &lt;description&gt;&lt;/description&gt;
    &lt;display-name&gt;TenServlet&lt;/display-name&gt;
    &lt;servlet-name&gt;TenServlet&lt;/servlet-name&gt;
    &lt;servlet-class&gt;exam.TenServlet&lt;/servlet-class&gt;
  &lt;/servlet&gt;
  &lt;servlet-mapping&gt;
    &lt;servlet-name&gt;TenServlet&lt;/servlet-name&gt;
    &lt;url-pattern&gt;/ten&lt;/url-pattern&gt;
  &lt;/servlet-mapping&gt;</code></pre>
<p>의미 : 클라이언트가 요청할 때 &quot;/ten&quot;이라는 url로 요청하게 되면, servlet-name이 같은 servlet을 찾아서 실제 클래스인 &quot;exam&quot;이라는 패키지 안에 있는 &quot;TenServlet&quot; 실행시켜주세요.</p>
<p>만약 찾지 못할 경우 404페이지를 리턴한다. 존재한다면, servlet-name을 보고 똑같은 이름의 servlet을 찾는다.</p>
<h2 id="서블릿-라이프사이클">서블릿 라이프사이클</h2>
<h4 id="학습목표-1">학습목표</h4>
<ul>
<li>서블릿의 생명주기를 이해한다.</li>
</ul>
<h3 id="lifecycleservlet-작성">LifecycleServlet 작성</h3>
<ul>
<li>서블릿 생명주기를 확인할 수 있는 LifecycleServlet을 작성한다.</li>
<li>HttpServlet의 3가지 메소드를 오버라이딩<ul>
<li>init()</li>
<li>service(request, response)</li>
<li>destroy()</li>
</ul>
</li>
</ul>
<h3 id="과정">과정</h3>
<ol>
<li>클라이언트가 url을 통해 서버에게 요청</li>
<li>서버는 url을 확인하여 mapping 되어있는 클래스를 찾음</li>
<li>해당 클래스가 메모리에 존재하는 지 확인</li>
<li>존재하면 service()를 호출하고 존재하지 않으면 클래스의 객체 생성</li>
</ol>
<p><img src="https://velog.velcdn.com/images/jmsuper_97/post/df9735e5-6419-4e8f-bfdd-ca2bdb02cabf/image.png" alt=""></p>
<p><code>init()</code> : 클래스의 생성자와 유사하게 클래스가 최초 생성될 때 1번 호출된다. 만약, destory()가 호출된 이후라면, 서블릿을 재생성하기 때문에 init()함수가 재호출된다.
<code>service()</code> : 클라이언트로 부터 요청이 있을 때마다 호출된다.
<code>destroy()</code> : 서버 코드의 수정과 같은 변화가 있거나, WAS가 종료될 때 호출된다.</p>
<h3 id="servicerequest-response-메소드">service(request, response) 메소드</h3>
<ul>
<li>HttpServlet의 service메소드는 템플릿 메소드 패턴으로 구현<ul>
<li>클라이언트의 요청이 GET일 경우에는 자신이 가지고 있는 doGet(request,response)를 호출</li>
<li>클라이언트의 요청이 POST일 경우에는 자신이 가지고 있는 doPost(request,response)를 호출</li>
</ul>
</li>
<li>즉, service 메소드는 클라이언트의 요청 종류에 따라 지정된 메소드를 호출한다.</li>
</ul>
<h4 id="servicerequest-response메소드의-oracle-document">service(request, response)메소드의 oracle document</h4>
<p><code>Receives standard HTTP requests from the public service method and dispatches them to the doXXX methods defined in this class.</code></p>
<br>


<h3 id="서블릿-라이프사이클-deep-dive">서블릿 라이프사이클 Deep Dive</h3>
<ul>
<li>참고링크 : <a href="https://www.geeksforgeeks.org/servlet-flow-of-execution/">https://www.geeksforgeeks.org/servlet-flow-of-execution/</a></li>
</ul>
<p>서블릿 컨테이너(ex. tomcat)에서 서블릿이 어떻게 생성되는지에 관해 더 깊이 알아보고자 한다.
현재 상황을 &#39;HTTP request가 서블릿 컨테이너에 전달되고 서블릿 컨테이너는 적절한 서블릿을 찾아 해당 서블릿을 생성하는 중&#39;이라고 가정한다.
<img src="https://velog.velcdn.com/images/jmsuper_97/post/ea6ff721-735f-46aa-8834-6987377a4cb2/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[인프라] Docker Overview 공식문서 번역]]></title>
            <link>https://velog.io/@jmsuper_97/%EC%9D%B8%ED%94%84%EB%9D%BC-Docker-Overview-%EA%B3%B5%EC%8B%9D%EB%AC%B8%EC%84%9C-%EB%B2%88%EC%97%AD</link>
            <guid>https://velog.io/@jmsuper_97/%EC%9D%B8%ED%94%84%EB%9D%BC-Docker-Overview-%EA%B3%B5%EC%8B%9D%EB%AC%B8%EC%84%9C-%EB%B2%88%EC%97%AD</guid>
            <pubDate>Sat, 19 Nov 2022 11:34:41 GMT</pubDate>
            <description><![CDATA[<p>이 글은 도커 공식문서를 번역한 글입니다. 오역이 존재할 수 있다는 점 참고 부탁드립니다.</p>
<p>공식문서 링크 : <a href="https://docs.docker.com/get-started/overview/">https://docs.docker.com/get-started/overview/</a></p>
<br>

<h3 id="docker-overview">Docker overview</h3>
<p>도커는 개발, 전송, 그리고 어플리케이션을 실행하기 위한 오픈 플랫폼이다. 도커는 어플리케이션을 인프라와 분리하므로써 소프트웨어를 더 빨리 전달할 수 있도록 한다. 도커와 함께라면, 어플리케이션을 관리하던 것과 동일한 방법으로 인프라를 관리할 수 있다. 코드를 빠르게 전송, 테스트, 배포하는 도커의 장점을 활용하면, 코드를 작성하고 배포환경에서 실제 실행하는 과정의 딜레이를 상당히 줄일 수 있다.</p>
<br>

<h3 id="the-docker-platform">The Docker platform</h3>
<p>도커는 ‘컨테이너’라고 불리는 느슨하게 결합된 환경에서 어플리케이션을 패키징하고 실행시키는 능력을 제공한다. 결합성과 보안성은 여러 컨테이너를 주어진 호스트에서 동시에 실행시킬 수 있도록 한다.</p>
<p>컨테이너는 가볍고, 어플리케이션을 실행하기 위한 모든 것을 포함하고 있다. 따라서, 호스트에 현재 어떤 것이 설치되있는지 신경쓰지 않아도 된다. 도커를 사용한다면 일을 하는 중에 컨테이너를 쉽게 공유할 수 있다. 그리고 컨테이너를 공유하는 모든 사람들은 동일한 방식으로 동작하는 컨테이너를 얻을 것이다.</p>
<p>도커는 컨테이너의 생성주기를 관리할 수 있도록 도구와 플랫폼을 제공한다.</p>
<ul>
<li>컨테이너를 사용하여 어플리케이션 및 지원 구성요소를 개발한다.</li>
<li>컨테이너는 어플리케이션을 분산시키고 테스트하기 위한 단위가 된다.</li>
<li>준비가 되었다면, 컨테이너 또는 오케스트레이션된 서비스로서 어플리케이션을 프로덕션 환경에서 배포한다. 프로덕션 환경이 로컬 데이터 센터 이거나 클라우드 환경인지와는 상관없이 동일하게 실행될 것이다.</li>
</ul>
<br>

<h3 id="what-can-i-use-docker-for">What can I use Docker for?</h3>
<ol>
<li><p>어플리케이션의 빠르고 일관된 배포</p>
<p> 도커는 개발자가 어플리케이션 및 서비스를 제공하는 로컬 컨테이너를 사용하여, 표준화된 환경에서 작업할 수 있도록 함으로써 개발 생명주기를 간소화 한다. 컨테이너는 CI/CD workflow에 적합하다.</p>
<p> 예시 시나리오</p>
<ul>
<li>개발자는 로컬 환경에서 코드를 작성하고 도커 컨테이너를 사용하여 동료들에게 그들의 work를 공유하다.</li>
<li>그들은 ‘테스트 환경에 어플리케이션을 집어넣기’ 그리고 ‘자동화되고 매뉴얼화된 테스트를 실행하기 ’ 위해 도커를 사용한다.</li>
<li>개발자가 버그를 발견할 경우, 그들은 개발 환경에서 버그를 고칠 수 있으며, 테스트와 확인을 위해 테스트 환경에 컨테이너를 재배포한다.</li>
<li>테스팅이 완료된 후, 서비스 이용자에게 업데이트된 버전을 제공하는 것은 업데이트된 이미지를 프로덕션 환경에 집어넣는 것 만으로 쉽게 해결 된다.</li>
</ul>
</li>
<li><p>신속한 구현 및 확장</p>
<p> 도커의 컨테이너 기반 플랫폼은 이동성이 높은 워크로드를 가능케 한다.도커 컨테이너는 개발자의 로컬 랩탑, 데이터 센터의 가상 머신, 클라우드 환경 등에서 실행될 수 있다.</p>
</li>
<li><p>동일한 하드웨어에서 더 많은 워크로드를 실행</p>
<p> 도커는 가볍고 빠르다. 다시말해, 하이퍼바이저 기반 가상 머신에 대한 실행 가능하고 비용 효율적인 대안을 제공하므로, 비즈니스 목표를 달성하기 위해 서버 용량을 더 많이 사용할 수 있다. 도커는 더 적은 리소스로 더 많은 작업을 수행해야 하는 고밀도 환경과 중소규모 구축에 이상적이다.</p>
</li>
</ol>
<br>

<h3 id="docker-architecture">Docker architecture</h3>
<p>도커는 <code>클라이언트-서버</code> 아키텍처를 사용한다. <code>도커 클라이언트</code>는 <code>도커 데몬</code> 과 대화한다. 도커 데몬은 도커 컨테이너를 만들고, 실행시키고, 분산시키는데 중요한 역할을 수행하는 녀석이다. 도커 컨테이너와 도커 데몬은 동일한 시스템에서 함께 실행될 수 있으며, 혹은 서로 다른 시스템에서 원격으로 연결될 수도 있다. 위 두 대상은 <code>UNIX 소켓</code> 또는 <code>네트워크 인터페이스</code> 위에서 REST API를 사용하여 통신한다. 또다른 도커 클라이언트로는 <code>Docker compose</code> 가 있으며, 컨테이너로 구성된 어플리케이션을 다룰 수 있도록 한다.</p>
<p><img src="https://velog.velcdn.com/images/jmsuper_97/post/75a7bb47-88be-4868-9960-c958a7dc763f/image.png" alt=""></p>
<br>

<h3 id="the-docker-daemon">The Docker daemon</h3>
<p>도커 데몬(<code>dockerd</code>)은 도커 API 요청을 듣고 있으며, 이미지 or 컨테이너 or 네트워크 or 볼륨 과 같은 도커 오브젝트를 관리한다. 또한 데몬은 도커 서비스를 관리하기 위해 다른 데몬과 통신할 수 있다.</p>
<br>

<h3 id="the-docker-client">The Docker client</h3>
<p>도커 클라이언트(<code>docker</code>)는 많은 도커 사용자가 도커와 상호작용하기 위해 사용하는 방식이다. 만약 <code>docker run</code> 과 같은 명령어를 사용할 경우, 클라이언트는 해당 명령어를 <code>dockerd</code> 에게 전송하게 된다. 도커 데몬이 명령어를 위임받아 수행할 것이다. <code>docker</code> 명령어는 도커 API를 사용한다. 도커 클라이언트는 하나 이상의 데몬들과 통신할 수 있다.</p>
<br>

<h3 id="docker-desktop">Docker Desktop</h3>
<p>도커 데스크탑은 Mac, Windows, Linux 환경에서 컨테이너화된 어플리케이션과 마이크로서비스를 생성하고 공유하도록 하는 ‘설치하기 쉬운’ 어플리케이션이다. 도커 데스크탑은 도커 데몬, 도커 클라이언트, 도커 컴포즈, 도커 컨텐트 트러스트, 쿠버네티스, 크레덴셜 헬퍼를 포함한다. </p>
<br>

<h3 id="docker-registries">Docker registries</h3>
<p>도커 레지스트리는 도커 이미지들을 저장한다. 도커 허브는 누구나 사용할 수 있는 <code>public registry</code> 이며, 도커는 기본적으로 도커 허브에서 이미지로서 찾아지도록 구성된다. 물론, <code>private registry</code> 를 실행 시킬 수도 있다.</p>
<p><code>docker pull</code> 또는 <code>docker run</code> 명령어를 사용한다면, 요청된 이미지는 사전에 정의된 레지스트리 장소에 당겨질(갱신될) 것이다. <code>docker push</code> 를 사용한다면, 레지스트리에 도커 이미지가 <code>push</code> 될 것이다.</p>
<br>

<h3 id="docker-objects">Docker objects</h3>
<p>도커를 사용할 때, 당신은 이미지, 컨테이너, 네트워크, 볼륨, 플러그인,그리고 그외 다른 객체들을 생성하고 사용할 것이다. 이번 섹션은 그러한 객체에 관한 overview이다.</p>
<h4 id="images">Images</h4>
<p>이미지는 도커 컨테이너를 생성하는 방법이 포함된 읽기 전용 템플리이다. 보통, 이미지는 다른 이미지를 기반으로 하며, 추가적인 커스텀 요소가 더해져 만들어 진다. 예를 들어, <code>ubuntu</code> 이미지를 기반으로한 이미지를 만들었지만, 아파치 웹 서버와 어플리케이션을 설치하고 어플리케이션의 실행을 위한 구성 세부 정보를 추가 설치할 수 있다.</p>
<p>당신은 당신만의 고유한 이미지를 만들거나, 다른 이들이 만들거나 레지스트리에 배포된 이미지만을 사용할 수 있다. 고유한 이미지를 만들기 위해서는, 이미지를 생성하고 실행시키기 위해 필요한 과정을 정의해 놓은 간단한 문법의 <code>Dockerfile</code> 을 만들어야 한다. <code>Dockerfile</code> 을 수정하거나 이미지를 재생성할 경우, 수정된 <code>layers</code> 만 재생성 될 것이다. 위와 같은 특징은 다른 가상화 기술과 비교했을 때, 이미지가 가볍고 and 작고 and  빠르게 하는 요인 중 하나이다.</p>
<h4 id="container">Container</h4>
<p>컨테이너는 이미지의 실제 실행가능한 인스턴스이다. 도커 API or CLI를 사용하여 컨테이너를 생성, 시작, 정지, 이동, 삭제를 할 수 있다. 당신은 하나 이상의 네트워크와 컨테이너를 연결 시킬 수 있으며, 스토리지에 컨테이너를 부착할 수 있고, 심지어 현재 상태를 기반으로 새로운 이미지를 만들 수도 있다.</p>
<p>기본적으로, 컨테이너는 <code>다른 컨테이너</code>와 <code>컨테이너가 실행되는 호스트 머신</code> 과 상대적으로 잘 고립되어있다. 당신은 컨테이너의 네트워크, 스토리지 또는 기타 기본 하위 시스템이 다른 컨테이너 또는 호스트 시스템과 얼마나 분리되어 있는지 제어할 수 있다.</p>
<p>컨테이너는 이미지뿐만 아니라 컨테이너를 생성하거나 시작할 때 제공하는 모든 구성 옵션에 의해 정의된다. 컨테이너가 제거되면 영구 저장소에 저장되지 않은 상태의 모든 변경 사항은 사라진다.</p>
<h4 id="example-docker-run-command">Example docker run command</h4>
<p>다음에 올 명령어는 <code>ubuntu</code> 컨테이너를 실행하고, 당신의 <code>command-line session</code> 과 양방형적으로 부착되며, <code>/bin/bash</code> 를 실행한다.</p>
<pre><code class="language-bash">$ docker run -i -t ubuntu /bin/bash</code></pre>
<p>위 명령어를 실행하면, 아래 사항들이 수행된다. (기본 레지스트리 설정을 사용한다고 가정한다.)</p>
<ol>
<li>로컬 환경에 <code>ubuntu</code> 이미지가 없다면, 도커는 수동으로 <code>docker pull ubuntu</code>를 실행한 것처럼 사전 정의된 레지스트리에서 이미지를 가져온다.</li>
<li>도커는 마치 <code>docker container create</code> 명령어를 실행한 것처럼 새로운 컨테이너를 만든다.</li>
<li>도커는 마지막 층으로 컨테이너에 읽기 전용 파일 시스템을 할당한다.</li>
<li>도커는 컨테이너와 기본 네트워크를 연결하는 네트워크 인터페이스를 생성한다. 왜냐하면 특정 네트워킹 옵션을 설정하지 않았기 때문이다. 이 과정은 컨테이너에 IP 주소를 할당하는 것을 포함한다. 기본적으로, 컨테이너는 호스트 머신의 네트워크 연결을 사용하여 외부 네트워크와 연결될 수 있다.</li>
<li>도커는 컨테이너를 시작하고, <code>/bin/bash</code> 를 실행한다. 컨테이너는 양방향으로 실행되고 있으며 당신의 터미널에 부착되기 때문에, 터미널에 출력문이 로깅되는 동안에 키보드를 사용하여 터미널에 입력을 수행할 수 있다.</li>
<li><code>exit</code> 명령어를 사용하여 <code>/bin/bash</code> 명령어를 종료하려고 한다면, 컨테이너는 정지되지만 삭제되지는 않는다. 다시 시작하거나 삭제할 수 있다.</li>
</ol>
<br>

<h3 id="the-underlying-technology">The underlying technology</h3>
<p>도커는 <code>Go</code> 언어로 만들어 졌다. 그리고 리눅스 커널의 여러 기능을 활용하여 기능을 제공한다. 도커는 <code>namespace</code> 라고 불리는 기술을 사용하여 <code>컨테이너</code> 라고 불리는 고립된 워크스페이스를 제공한다. 컨테이너를 실행하면, 도커는 해당 컨테이너에 대한 네임스페이스 집합을 만든다.</p>
<p>이 네임스페이스들은 고립층을 제공한다. 컨테이너의 각 측면은 분리된 네임스페이스에서 실행되며, 컨테이너에서의 접근은 컨테이네의 네임스페이스로 한정된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[네트워크] 2. 웹 백엔드 프로그래밍 기초 - 웹 서버 & WAS]]></title>
            <link>https://velog.io/@jmsuper_97/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EB%B6%80%EC%8A%A4%ED%8A%B8%EC%BD%94%EC%8A%A4-%EC%9B%B9%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B3%BC%EC%A0%95-2.-%EC%9B%B9-%EB%B0%B1%EC%97%94%EB%93%9C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EA%B8%B0%EC%B4%88-%EC%9B%B9-%EC%84%9C%EB%B2%84-WAS-q3bhk33b</link>
            <guid>https://velog.io/@jmsuper_97/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EB%B6%80%EC%8A%A4%ED%8A%B8%EC%BD%94%EC%8A%A4-%EC%9B%B9%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B3%BC%EC%A0%95-2.-%EC%9B%B9-%EB%B0%B1%EC%97%94%EB%93%9C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EA%B8%B0%EC%B4%88-%EC%9B%B9-%EC%84%9C%EB%B2%84-WAS-q3bhk33b</guid>
            <pubDate>Wed, 16 Nov 2022 08:00:34 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>참고자료</p>
</blockquote>
<ul>
<li>웹 서버를 사용하는 이유 : <a href="https://www.quora.com/Why-do-we-need-a-web-server-when-we-already-have-application-server">https://www.quora.com/Why-do-we-need-a-web-server-when-we-already-have-application-server</a></li>
<li>웹 서버 : <a href="https://loopstudy.tistory.com/159">https://loopstudy.tistory.com/159</a></li>
<li>apache와 nginx 비교 : <a href="https://velog.io/@deannn/Apache%EC%99%80-NginX-%EB%B9%84%EA%B5%90-%EC%B0%A8%EC%9D%B4%EC%A0%90">https://velog.io/@deannn/Apache%EC%99%80-NginX-%EB%B9%84%EA%B5%90-%EC%B0%A8%EC%9D%B4%EC%A0%90</a></li>
<li>servlet container : <a href="https://www.javatpoint.com/container">https://www.javatpoint.com/container</a></li>
<li><a href="https://maenco.tistory.com/entry/%EC%84%9C%EB%B8%94%EB%A6%BFServlet-%EA%B3%BC-%EC%84%9C%EB%B8%94%EB%A6%BF-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88Servlet-Container?category=965893">https://maenco.tistory.com/entry/%EC%84%9C%EB%B8%94%EB%A6%BFServlet-%EA%B3%BC-%EC%84%9C%EB%B8%94%EB%A6%BF-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88Servlet-Container?category=965893</a></li>
</ul>
<br>

<h2 id="웹서버">웹서버</h2>
<h3 id="웹-서버란">웹 서버란?</h3>
<ul>
<li>웹 서버는 소프트웨어를 보통 말하지만, 웹 서버 소프트웨어가 동작하는 컴퓨터를 말한다.</li>
<li>웹 서버의 가장 중요한 기능은 클라이언트(Client)가 요청하는 HTML문서나 각종 리소스(Resource)를 전달하는 것이다. HTTP에 맞도록 리소스를 요청하게되고, 웹서버는 요청한 리소스를 반환한다.</li>
<li>웹 브라우저나 웹 크롤러가 요청하는 리소스는 컴퓨터에 저장되어 있는 정적(static)데이터이거나 동적인 결과가 될 수 있다. 정적인 데이터는 이미지, html파일, css파일,js파일을의미한다. 동적인 결과는 프로그램을 통해서 만들어지는 결과를 말한다.</li>
<li>웹 크롤러란? 네이버나 구글같은 검색사이트에서 다른 사이트의 정보를 읽어갈 때 사용하는소프트웨어이다.</li>
</ul>
<blockquote>
<p>자바로 실제 HTTP 서버를 구현할 수 있다. 하지만, 복잡한 과정을 일일이 개발해야 하기 때문에 cost가 든다. java로 구현한 http 서버는 아래 링크를 참고하자.
링크 : <a href="https://velog.io/@jmsuper_97/Java-%EC%9E%90%EB%B0%94%EB%A1%9C-%EA%B5%AC%ED%98%84%ED%95%98%EB%8A%94-HTTP-%EC%84%9C%EB%B2%84">https://velog.io/@jmsuper_97/Java-%EC%9E%90%EB%B0%94%EB%A1%9C-%EA%B5%AC%ED%98%84%ED%95%98%EB%8A%94-HTTP-%EC%84%9C%EB%B2%84</a></p>
</blockquote>
<br>

<h3 id="웹-서버를-사용하는-이유">웹 서버를 사용하는 이유</h3>
<p> 웹 서버의 주된 기능은 웹 페이지를 클라이언트에게 전달하는 것이다. 그러나 WAS에서도 기본적으로 위와 같은 기능을 수행하고 있는데, 웹 서버가 필요한 이유는 무엇일까? 정적 및 동적 데이터를 응답하는 기능을 WAS에서 모두 수행할 경우 서버 과부화 우려가 존재한다. 또한 WAS에 장애가 발생할 경우, 정적 페이지를 응답할 수 었게 된다. 사용자가 극히 적은 서비스 초기에는 WAS만을 사용하는 것이 큰 문제가 되지 않는다. 하지만, 트래픽이 늘어났을 때, 이를 모니터링하고 서버를 증설하기 위해서는 <code>WAS-DB</code> 구조 보다는 <code>Web Server-WAS-DB</code> 구조가 더 효율적이다.</p>
<br> 

<h3 id="웹-서버의-종류">웹 서버의 종류</h3>
<ul>
<li>가장 많이 사용하는 웹 서버는 Apache, Nginx, Microsoft, Google 웹서버가 있다.</li>
</ul>
<h4 id="apache">Apache</h4>
<p>Apachee Software Foundation에서 개발한 웹서버로 오픈소스 S/W이며 거의 대부분의 운영체제에서 설치 및 사용이 가능하다. </p>
<ul>
<li><p>스레드 / 프로세스 기반 구조</p>
<ul>
<li>클라이언트 요청당 하나의 스레드가 처리하는 구조이다.<ul>
<li>사용자가 많으면 스레드 생성, 메모리 및 CPU 낭비가 심하다.</li>
</ul>
</li>
</ul>
</li>
<li><p>MPM(Multi-Process Module)</p>
<ul>
<li><p>Prefork MPM(다중 프로세스)
<img src="https://velog.velcdn.com/images/jmsuper_97/post/d72ddac3-4909-4c18-b382-8e24fca50cf0/image.png" alt=""></p>
<ul>
<li><p>Client 요청에 대해 Apache 자식 프로세스를 생성하여 처리한다.</p>
</li>
<li><p>하나의 자식 프로세스 당 하나의 스레드를 갖는 구조로, 자식 프로세스는 최대 1024까지 가능</p>
</li>
<li><p>스레드 간 메모리를 공유하지 않는다. 따라서 메모리 소모가 크다</p>
</li>
<li><p>응답 프로세스를 미리 띄어놓고 클라이언트 요청 시 자식 프로세스가 반응하는 방식이다.</p>
</li>
<li><p>Single CPU 또는 Dual CPU에서 성능이 좋다.</p>
<br></li>
<li><p>Worker MPM(멀티 프로세스-스레드)
<img src="https://velog.velcdn.com/images/jmsuper_97/post/2daca3e8-bb5b-4fb0-b35e-c627a0ecf098/image.png" alt=""></p>
<ul>
<li>Prefork보다 메모리 사용량이 적고, 동시 접속자가 많은 사이트에 적합하다.</li>
<li>각 프로세스의 스레드를 생성해 처리하는 구조이다.</li>
<li>스레드 간의 메모리 공유가 가능하다.</li>
<li>프로세스 당 최대 64개의 스레드 처리가 가능하며, 각 스레드는 하나의 연결만을 갖는다.</li>
<li>Multi CPU 시스템에서 성능이 좋다.</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<br>

<h4 id="nginx">Nginx</h4>
<p>Nginx는 차세대 웹서버로 보통 불리며 더 적은 자원으로 더 빠르게 데이터를 서비스하는 것을 목적으로 만들어진 서버이며 Apache웹 서버와마찬가지로 오픈소스 소프트웨어이다. <strong>Event-Driven 구조</strong>이다. 즉, 프로그램의 흐름이 이벤트에 의해 결정되는 방식이다.
<img src="https://velog.velcdn.com/images/jmsuper_97/post/25c670cb-f37f-4b6f-9241-5af8079f5ce1/image.png" alt=""></p>
<ul>
<li>한 개 또는 고정된 프로세스만 생성하고, 여러 개의 Connection을 모두 Event-handler를 통해 비동기 방식으로 처리한다.</li>
<li>적은 양의 스레드만 사용되기 때문에 Context Switching 비용이 적고, CPU 소모가 적다.</li>
<li>Apache와 달리 동시 접속자 수가 많아져도, 추가 비용이 들지 않는다.</li>
<li>CPU와 관계없이 모든 I/O들을 전부 Event Listener로 미루기 때문에 흐름이 끊기지 않고 응답이 빠르게 진행되어 1개의 프로세스로 더 빠른 작업이 가능</li>
<li>외부 프로세서로 전달하고 렌더링 된 컨텐츠를 다시 전송할 때까지 기다려야 하여, 프로세스 속도 저하가 존재한다.</li>
</ul>
<br>

<h4 id="apache와-nginx-비교">Apache와 Nginx 비교</h4>
<table>
<thead>
<tr>
<th>Apache</th>
<th>NginX</th>
</tr>
</thead>
<tbody><tr>
<td>요청 당 스레드 또는 프로세스가 처리</td>
<td>비동기 이벤트 기반으로 처리</td>
</tr>
<tr>
<td>CPU/메모리 자원 낭비 심함</td>
<td>CPU/메모리 지원 사용률 낮음</td>
</tr>
<tr>
<td>NginX보다 모듈이 다양</td>
<td>Apache에 비해 다양한 모듈이 없음</td>
</tr>
<tr>
<td>PHP 모듈 등 직접 적재 가능</td>
<td>많은 접속자들 대응 가능</td>
</tr>
<tr>
<td>안정성, 확장성, 호환성 우세</td>
<td>성능 우세</td>
</tr>
<tr>
<td>동적 컨텐츠 단독 처리 가능</td>
<td>동적 컨텐츠 단독 처리 불가능</td>
</tr>
</tbody></table>
<p><br><br></p>
<h2 id="was">WAS</h2>
<h3 id="학습-목표">학습 목표</h3>
<ul>
<li>WAS란?</li>
<li>WAS의 역할</li>
<li>웹서버와 WAS의 차이</li>
</ul>
<br>

<h3 id="클라이언트서버-구조">클라이언트/서버 구조</h3>
<ul>
<li>클라이언트는 서비스를 제공하는 서버에게 정보를 요청하여 응답 받은 결과를 사용한다.</li>
<li>대표적인 예시로 브라우저와 웹서버의 관계가 있다.</li>
</ul>
<br>

<h3 id="dbms">DBMS</h3>
<ul>
<li>다수의 사용자들이 데이터베이스 내의 데이터를 접근할 수 있도록 해주는 소프트웨어</li>
<li>DBMS에 접속해서 동작하는 클라이언트 프로그램이 한 때 많이 만들어졌다. 그런데, 이러한 구조는 클라이언트의 로직이 많아지고 프로그램이 커진다는 문제가 있었다.
또한 프로그램의 로직이 변경되면 매번 클라이언트가 매번 배포되어야 한다는 문제가 있었다. 대부분의 로직이 클라이언트에 포함되어 보안상 취약하다는 단점이 있었다.</li>
</ul>
<br>

<h3 id="미들웨어middleware">미들웨어(MiddleWare)</h3>
<p>위에서의 문제점을 해결하기 위해 미들웨어가 등장하였다.</p>
<ul>
<li>클라이언트 쪽에 비즈니스 로직이 많을 경우, 클라이언트 관리(배포 등)으로 인해 비용이 많이 발생하는 문제가 발생</li>
<li>비즈니스 로직을 클라이언트와 DBMS사이의 미들웨어 서버에서 동작하도록 함으로써 클라이언트는 입력과 출력만 담당하도록 한다.</li>
<li>비즈니스 로직 수정시 미들웨어만 변경하면 되는 장점을 가지게 된다.<br>
<img src="https://velog.velcdn.com/images/jmsuper_97/post/ffeb188b-8744-46ea-b6b6-2f0760717742/image.png" alt=""></li>
</ul>
<br>

<h3 id="wasweb-application-server">WAS(Web Application Server)</h3>
<ul>
<li>동적인 기능은 웹서버에 프로그램이 들어가는 방식인 CGI를 통해 가능했었다.</li>
<li>그러나, Web이 복잡해지면서 브라우저와 DBMS 사이에서 동작하는 미들웨어가 필요해졌다.</li>
<li>WAS는 일종의 미들웨어로 웹 클라이언트(보통 웹 브라우저)의 요청 중 보통 웹 애플리케이션이 동작하도록 지원하는 목적을 가진다.</li>
<li>WAS의 중요한 기능 3가지<ul>
<li>프로그램 실행 환경과 데이터베이스 접속 기능을 제공한다.</li>
<li>여러 트랜잭션을 관리한다.</li>
<li>업무를 처리하는 비즈니스 로직을 수행한다.</li>
</ul>
</li>
<li>Web Server의 기능을 제공한다.</li>
</ul>
<br>

<h3 id="was의-역할">WAS의 역할</h3>
<ul>
<li>Life Cycle Management : 서블릿의 생명주기를 관리한다. 필요한 서블릿을 생성하고 제거한다.</li>
<li>Multithreaded support : 클라이언트의 요청은 쓰레드에서 처리한다. WAS는 이러한 쓰레드를 관리한다.</li>
<li>Communicate with web server : 웹 서버와 통신한다. 톰캣의 경우 소켓을 생성하여 웹서버와 서블릿 컨테이너를 연결해준다.</li>
<li>Security etc.</li>
</ul>
<br>

<h3 id="웹-서버-vs-was">웹 서버 vs WAS</h3>
<ul>
<li>WAS도 보통 자체적으로 웹 서버 기능을 내장하고 있다.</li>
<li>현재는 WAS가 가지고 있는 웹 서버도 정적인 컨텐츠를 처리하는데 있어서 성능상 큰 차이가 없다.</li>
<li>규모가 커질수록 웹 서버와 WAS를 분리한다. 그 목적은 장애 극복 기능(failover)인 경우가 많다.</li>
<li>웹 서버가 WAS 앞 단에 있는 것이 좋다. 웹 서버는 상대적으로 WAS보다 간단한 구조로 만들어져 있다. 사람들이 사용하는 대용량 프로그램은 서버의 수가 여러 대일 수 있다.
WAS에서 동작하는 프로그램이 오작동을 발생하여 문제를 일으킬 경우가 있을 수 있다. 그 경우 WAS를 재부팅해야 될 수 있다. 웹 서버를 앞단에 두고 해당 WAS를 이용하지 못하다록 하고 WAS를 재부팅한다면, 해당 웹 서버를 사용하는 사람은 WAS에 문제가 발생한 것을 인지하지 못하게 된다.
이러한 기능을 장애 극복 기능이라고 한다. 무중단으로 실행하도록 하기 위해서 이 기능이 필요하다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[원격 서버에 SpringBoot를 백그라운드로 배포하는 방법]]></title>
            <link>https://velog.io/@jmsuper_97/%EC%9B%90%EA%B2%A9-%EC%84%9C%EB%B2%84%EC%97%90-SpringBoot%EB%A5%BC-%EB%B0%B1%EA%B7%B8%EB%9D%BC%EC%9A%B4%EB%93%9C%EB%A1%9C-%EB%B0%B0%ED%8F%AC%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@jmsuper_97/%EC%9B%90%EA%B2%A9-%EC%84%9C%EB%B2%84%EC%97%90-SpringBoot%EB%A5%BC-%EB%B0%B1%EA%B7%B8%EB%9D%BC%EC%9A%B4%EB%93%9C%EB%A1%9C-%EB%B0%B0%ED%8F%AC%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Tue, 15 Nov 2022 05:23:43 GMT</pubDate>
            <description><![CDATA[<h3 id="서버-배포-정리">서버 배포 정리</h3>
<ul>
<li>nohup 명령어를 사용해 서버 실행 중 로그를 저장</li>
<li>원래 nohup은 백그라운드로 실행시키는 명령어인데, 서버에서 제대로 작동하지 않아 screen 명령어를 사용</li>
<li>screen 명령어는 가상 쉘을 만들어 putty에서 나가도 프로세스가 종료되지 않게 함</li>
<li>jar 파일을 가져와 nohup로 실행시키기 전에 jar 파일 권한을 <strong>chmod 755 &quot;jar 파일명&quot;</strong>을 통해 <strong>755</strong>로 수정해야 함</li>
</ul>
<hr>
<h3 id="서버-배포-방법">서버 배포 방법</h3>
<ol>
<li><code>screen -list</code>:  현재 서버에 어떤 screen(가상쉘)이 존재하는지 확인. 현재는 &quot;26113.plming&quot;이라는 쉘에서 jar를 실행시킴</li>
<li><code>screen -x 26113.plming</code>: 가상 쉘에 접속</li>
<li><code>ps ax | grep jar</code> : 실행 중인 프로세스 확인<ul>
<li>실행 중인 프로세스가 있는 경우 <code>sudo kill -9 &quot;프로세스 번호&quot;</code>를 입력해 프로세스를 중단시켜야 함</li>
<li>프로세스를 중단한 다음 같은 명령어를 실행해 실행 중인 프로세스가 없는지 한 번 더 확인</li>
</ul>
</li>
<li><code>sudo java -jar board-0.0.1-SNAPSHOT.jar</code>: 백그라운드에 실행하기 전 빌드 파일이 잘 실행되는지 확인</li>
<li><code>sudo nohup java -jar board-0.0.1.jar &amp;</code>: 백그라운드에서 jar를 실행. screen에 접속해 jar 파일을 실행시킬 경우 반드시 sudo 권한으로 실행시켜야 함. background 폴더에 nohup.log 파일이 있는데, 이 파일에 실행 로그가 저장됨 </li>
<li>ctrl+a -&gt; ctrl+d: screen에서 나가는 명령어. putty 종료해도 프로세스는 백그라운드에서 잘 동작하기 때문에 나가지 않아도 괜찮음</li>
</ol>
<hr>
<ul>
<li>screen 명령어 참고 사이트: <a href="https://koos808.tistory.com/90">https://koos808.tistory.com/90</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[AI] 규칙 기반 챗봇 with Python]]></title>
            <link>https://velog.io/@jmsuper_97/AI-%EA%B7%9C%EC%B9%99-%EA%B8%B0%EB%B0%98-%EC%B1%97%EB%B4%87-with-Python</link>
            <guid>https://velog.io/@jmsuper_97/AI-%EA%B7%9C%EC%B9%99-%EA%B8%B0%EB%B0%98-%EC%B1%97%EB%B4%87-with-Python</guid>
            <pubDate>Mon, 14 Nov 2022 10:25:53 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>참고자료
    - <a href="https://datasciencedojo.com/blog/rule-based-chatbot-in-python/">https://datasciencedojo.com/blog/rule-based-chatbot-in-python/</a>
    - <a href="https://frhyme.github.io/python-lib/nltk-wordnet/">https://frhyme.github.io/python-lib/nltk-wordnet/</a>
    - <a href="https://regexr.com/">https://regexr.com/</a></p>
</blockquote>
<h2 id="목차">목차</h2>
<ul>
<li>챗봇이란?</li>
<li>챗봇 종류</li>
<li>Rule-based 챗봇을 만들기 위한 도구들</li>
<li>챗봇 실행 과정</li>
<li>파이썬을 이용한 챗봇 구현</li>
</ul>
<br>

<h2 id="챗봇이란">챗봇이란?</h2>
<blockquote>
<p>출처 : <a href="https://namu.wiki/w/%EC%B1%97%EB%B4%87">https://namu.wiki/w/챗봇</a></p>
</blockquote>
<p>메신저에서 유저와 소통하는 봇 을 말한다. 단순히 정해진 규칙에 맞춰서 메시지를 입력하면 발화를 출력하는 단순한 챗봇에서부터 상대방의 발화를 분석하여 인공지능에 가까운 발화를 내놓는 챗봇까지 다양한 챗봇들이 있다.</p>
<p>본래, 메시지를 규칙 기반으로 송출해주는 정도에 지나지 않았으나, 2016년에 있었던 구글 딥마인드와 알파고의 등장 이후 챗봇에도 AI 기술을 접목시키려는 시도가 활발하게 이루어지고 있다. 또한, 기업에서도 큰 관심을 보여 각종 은행, 보험회사 등의 상담 챗봇 등이 다수 등장하였다.</p>
<p>Siri나 빅스비같은 음성 인식 비서 서비스도 큰틀에서 따지자면 챗봇을 내장한 형태라 할 수 있다. 내부적으로 챗봇에 음성인식 기술을 접목시켜, 사용자가 말한 내용을 컴퓨터가 이해할 수 있도록 텍스트로 변환하고 이를 질의로 보내 답을 발화시키고 TTS 기술로 음성으로 바꾸는 것. 물론 순수한 챗봇은 음성인식 기술이 없이 사용자가 입력한 텍스트만을 다룬다.</p>
<br>

<h2 id="챗봇-종류">챗봇 종류</h2>
<h3 id="rule-based-chatbots">Rule-based Chatbots</h3>
<p>규칙 기반 챗봇은 미리 저장된 응답 데이터베이스를 사용한다. 사전에 정의된 규칙에 따라 사용자의 입력은 적절한 응답으로 매칭된다. 이 때문에, 규칙 기반 챗봇은 고유한 답변을 생성하지 못한다. 하지만, 응답 데이터베이스의 규모가 충분히 크고 규칙이 잘 정립되었을 경우, 충분히 생산성있고 유용하게 사용될 수 있다.</p>
<p>규칙 기반 챗봇의 간단한 형태는 사용자 입력과 응답에 대해 1대ㅐ1 테이블 구조를 갖고 있다. 이 봇은 매우 제한적으로 행동하며, 오직 사전에 정의된 응답만 답변할 수 있다는 한계점을 갖는다.</p>
<h3 id="ai-based-chatbots">AI-based chatbots</h3>
<p>최근 머신러닝이 발전하면서, 챗봇을 구현하는 새로운 접근법이 등장하였다. 인공지능을 활용하여 매우 정밀하고 직관적인 챗봇을 구현할 수 있게 되었다. 규칙 기반 챗봇과는 다르게, AI 기반 챗봇은 복잡한 머신러닝 모델을 기반으로 하며 스스로 학습하는 것이 가능하다.</p>
<p><br><br></p>
<h2 id="rule-based-챗봇을-만들기-위한-도구들">Rule-based 챗봇을 만들기 위한 도구들</h2>
<h4 id="natural-language-toolkitnltk">Natural Language Toolkit(NLTK)</h4>
<p>NLTK는 인간의 언어 데이터를 쉽게 처리하도록 도와주는 파이썬 라이브러리이다. 즉, 자연어 처리를 도와주는 도구이다.</p>
<h4 id="regular-expressionregex-in-python">Regular Expression(RegEx) in Python</h4>
<p>정규 표현식은 <code>단어</code> or <code>문장</code> 등에서 특정한 패턴을 검색하고 찾아주는데 도움을 주는 식이다. 해당 식은 문자들의 특정한 나열로 이뤄진다. 정규 표현식은 UNIX에서 텍스트를 검색하고 매칭하는데 사용된다. 파이썬에서 <code>re</code> 라이브러리를 사용한다.</p>
<p><br><br></p>
<h2 id="챗봇-실행-과정">챗봇 실행 과정</h2>
<p><img src="https://velog.velcdn.com/images/jmsuper_97/post/9d9b0763-dbaf-41dc-bc5d-bf3473ee1037/image.png" alt=""></p>
<blockquote>
</blockquote>
<ol>
<li>사용자 입력</li>
<li>키워드를 중심으로 입력된 데이터를 탐색</li>
<li>입력을 키워드를 기반으로 한 <code>특정 의도</code> 와 매칭</li>
<li><code>매칭된 의도</code> 에 해당하는 응답을 선택</li>
<li>응답을 출력</li>
</ol>
<p><code>과정 3</code> 에서 키워드를 <code>특정 의도</code> 와 매칭하기 위해 <code>NLTK</code> 를 사용한다. <code>NLTK</code> 에서 제공하는 <code>wordnet</code> 을 활용하여 <code>특정 의도(Intent)</code> 에 해당하는 동의어들을 알아낸다. 사용자는 챗봇의 규칙을 알지 못하기 때문에, 임의의 단어들을 조합하여 입력을 수행할 것이다. 따라서 챗봇은 사용자의 입력들에서 키워드를 추출한 이후, 해당 키워드들이 어떤 <code>특정 의도(Intent)</code> 와 가장 연관있는 지 알아내야 한다. (이 부분이 챗봇의 성능을 좌우할 것 같다.)</p>
<p>챗봇 실행 과정 중, 가장 핵심적으로 개발해야 하는 부분은 <code>과정 3</code> 에서 사용될 <code>추론 엔진</code> 이다. <code>특정 의도(Intent)</code> 와 매칭되는 응답을 미리 정의하는 것은 엑셀로 표현될 만큼 간단하다. 하지만, <code>추론 엔진</code> 은 임의의 입력값이 어떤 <code>의도</code> 와 매칭되는 지 알고리즘을 개발해야 하기 때문에 심의를 기울여야 한다.</p>
<p><br><br></p>
<h2 id="파이썬을-이용한-챗봇-구현">파이썬을 이용한 챗봇 구현</h2>
<p>간단한 챗봇을 구현하기 위해 <code>은행 서비스</code> 를 가정하겠다. 은행 서비스에서 제공할 <code>Intent</code>와 <code>Response</code>는 다음과 같다.</p>
<table>
<thead>
<tr>
<th>Intent</th>
<th>Response</th>
</tr>
</thead>
<tbody><tr>
<td>Greet</td>
<td>Hi! How can I help you?</td>
</tr>
<tr>
<td>Timings</td>
<td>We are open from 9AM to 5PM.</td>
</tr>
</tbody></table>
<p>챗봇 구현 과정은 다음과 같다.</p>
<blockquote>
</blockquote>
<ol>
<li>Importing Dependencies</li>
<li>Building the Keyword List</li>
<li>Building a dictionary of Intents</li>
<li>Defining a dictionary of responses</li>
<li>Matching Intents and Generating Responses</li>
</ol>
<h3 id="importing-dependencies">Importing Dependencies</h3>
<pre><code class="language-python">import re
# import nltk
# nltk.download()
from nltk.corpus import wordnet</code></pre>
<p><code>wordnet</code>은 단어들 간의 의미론적인 관계를 정의한 어휘 데이터베이스이다. 이후 <code>wordnet</code> 을 활용하여 <code>Intent</code> 로 정의해 놓은 키워드들의 동의어들을 추출한 이후, 해당 동의어들을 value로 하는 <code>a dictionary of synonyms</code> 를 생성할 것이다. 이를 통해 직접 사용자가 사용할 가능성이 있는 단어들을 사전에 정의할 필요없이, 동의어 확장이 가능하다.</p>
<blockquote>
<p>conda 환경 사용 시 nltk의 특정 부분이 설치되지 않아 프로그램이 실행되지 않는 현상을 발견하였다. 이를 해결하고자, 소스코드 내에서 명시적으로 nltk를 다운로드하였다.</p>
</blockquote>
<br>

<h3 id="building-the-keyword-list">Building the Keyword List</h3>
<pre><code class="language-python"># Building a list of keywords
list_words = [&#39;hello&#39;,&#39;timings&#39;]
list_syn = {}
for word in list_words:
    sysnonyms = []
    for syn in wordnet.synsets(word):
        for lem in syn.lemmas():
            # Remove any special charaters from sysnonym strings
            lem_name = re.sub(&#39;[^a-zA-Z0-9 \n\.]&#39;,&#39; &#39;,lem.name())
            sysnonyms.append(lem_name)
    list_syn[word]=set(sysnonyms)</code></pre>
<p><code>Keyword List</code> 는 챗봇이 사용자의 입력을 받은 이후 탐색할 대상이다. 많은 <code>Keyword</code> 를 추가할 수록, 사용자의 입력에 대해 적절한 응답을 수행할 가능성이 많아지기 때문에, 결과적으로 챗봇이 성능이 올라갈 것이다. 앞에서 말했듯이, <code>wordnet</code> 을 사용하여 <code>Intent</code> 의 동의어들을 <code>dictionary</code> 구조에 저장할 것이다. 소스코드에서 <code>list_syn</code> 이 저장될 변수이다.</p>
<p>위 소스코드에서 각 함수들과 단어들이 무엇을 의미하는지 확인해보자.</p>
<ul>
<li><p><code>synset</code> : <code>wordnet</code> 을 탐색하기 위해 <code>NLTK</code> 에서 제공하는 인터페이스이다. <code>synset</code> 인스턴스는 동의의들을 그룹화한 것이다.</p>
<pre><code>  Synset instance
  - name : 인스턴스 이름(hello.n.01)
  - meaning : 기준이 되는 동의어(an expression of greeting)
  - example : 예시([&#39;every morning they exchanged polite hellos&#39;])</code></pre></li>
<li><p><code>wordnet.synsets(word)</code> : <code>list of synset</code> 을 반환한다. 이 리스트는 비어있을 수 있으며, 여러 item들을 가지고 있을 수도 있다.</p>
</li>
<li><p><code>syn.lemmas()</code> : synset에 포함되어 있는 단어 기본형 리스트(lemma는 단어의 기본형태 정도로 표현할 수 있을 것 같다)</p>
</li>
</ul>
<blockquote>
<h4 id="list_syn에-저장된-데이터">list_syn에 저장된 데이터</h4>
<p>hello
{&#39;hello&#39;, &#39;howdy&#39;, &#39;hi&#39;, &#39;hullo&#39;, &#39;how do you do&#39;}
timings
{&#39;time&#39;, &#39;clock&#39;, &#39;timing&#39;}</p>
</blockquote>
<br>

<h3 id="building-a-dictionary-of-intents">Building a dictionary of Intents</h3>
<pre><code class="language-python"># Building dictionary of Intents &amp; Keywords
keywords = {}
keywords_dict = {}

# Defining a new key in the keywords dictionary
keywords[&#39;greet&#39;] = []
# Populating the values in the keywords dictionary with synonyms of keywords
# formatted with RegEx metacharacters
for synonym in list(list_syn[&#39;hello&#39;]):
    keywords[&#39;greet&#39;].append(&#39;.*\\b&#39; + synonym + &#39;\\b.*&#39;)

# Defining a new key in the keywords dictionary
keywords[&#39;timings&#39;] = []
for synonym in list(list_syn[&#39;timings&#39;]):
    keywords[&#39;timings&#39;].append(&#39;.*\\b&#39; + synonym + &#39;\\b.*&#39;)

for intent, keys in keywords.items():
    # Joining the values in the keywords dictionary with the OR(|) operator updating
    # them in keywords_dict dictionary
    keywords_dict[intent]=re.compile(&#39;|&#39;.join(keys))</code></pre>
<p>앞서 지정한 <code>list_syn</code>(키워드의 동의어 집합 리스트)을 <code>Intent</code> 와 매칭 시키는 <code>dictionary</code> 를 생성해야 한다. <code>list_syn</code> 은 사용자가 입력할 가능성이 있는 키워드를 정의해놓은 데이터라면, <code>keywords_dict</code> 는 해당 키워드들이 실제 <code>Intent</code> 와 매칭될 수 있도록 정의하는 자료구조이다.</p>
<ul>
<li><code>\b</code> : Word boundary. Matches a word boundary position between a word character and non-word character or position (Start / end of string)<a href="https://regexr.com/">https://regexr.com/</a></li>
<li><code>|</code> : Alternation. Acts like a boolean OR. Matches the expression before or after the <code>|</code></li>
</ul>
<br>

<h3 id="defining-a-dictionary-of-responses">Defining a dictionary of responses</h3>
<pre><code class="language-python"># Building a dictionary of responses
responses={
    &#39;greet&#39; : &#39;Hello! How can I help you?&#39;,
    &#39;timings&#39;  : &#39;We are open from 9AM to 5PM, Monday to Friday. We are closed on weekends and public holidays&#39;,
    &#39;fallback&#39; : &#39;I dont quite understand. Could you repeat that?&#39;
}</code></pre>
<br>

<h3 id="matching-intents-and-generating-responses">Matching Intents and Generating Responses</h3>
<pre><code class="language-python">print(&quot;Welcome to MyBank. How may I help you?&quot;)
# While loop to run the chatbot indefinetely
while True:
    # Takes the user input and converts all characters to lowercase
    user_input = input().lower()
    # Defining the Chatbot&#39;s exit condition
    if user_input == &#39;quit&#39;:
        print(&#39;Thank you for visiting.&#39;)
        break
    matched_intent = None
    for intent, pattern in keywords_dict.items():
        # Using the regular expression search function to look for keywords in user input
        if re.search(pattern, user_input):
            # if a keyword matches, select the corresponding intent from the keywords_dict dictionary
            matched_intent = intent
    # The fallback intent is selected by defualt
    key = &#39;fallback&#39;
    if matched_intent in responses:
        # If a keyword matches, the fallback intent is replaced by
        # the matched intent as the key for the responses dictionary
        key = matched_intent
    # The chatbot prints the response that matches the seleted intent
    print(responses[key])</code></pre>
<p><br><br></p>
<h2 id="전체-소스코드">전체 소스코드</h2>
<pre><code class="language-python">import re
import nltk
# nltk.download()
from nltk.corpus import wordnet

# Building a list of keywords
list_words = [&#39;hello&#39;,&#39;timings&#39;]
list_syn = {}
for word in list_words:
    sysnonyms = []
    for syn in wordnet.synsets(word):
        for lem in syn.lemmas():
            # Remove any special charaters from sysnonym strings
            lem_name = re.sub(&#39;[^a-zA-Z0-9 \n\.]&#39;,&#39; &#39;,lem.name())
            sysnonyms.append(lem_name)
    list_syn[word]=set(sysnonyms)

# Building dictionary of Intents &amp; Keywords
keywords = {}
keywords_dict = {}
# Defining a new key in the keywords dictionary
keywords[&#39;greet&#39;] = []
# Populating the values in the keywords dictionary with synonyms of keywords
# formatted with RegEx metacharacters
for synonym in list(list_syn[&#39;hello&#39;]):
    keywords[&#39;greet&#39;].append(&#39;.*\\b&#39; + synonym + &#39;\\b.*&#39;)

# Defining a new key in the keywords dictionary
keywords[&#39;timings&#39;] = []
# Populating the values in the keywords dictionary with synonyms of keywords
# formatted with RegEx metacharacters
for synonym in list(list_syn[&#39;timings&#39;]):
    keywords[&#39;timings&#39;].append(&#39;.*\\b&#39; + synonym + &#39;\\b.*&#39;)

for intent, keys in keywords.items():
    # Joining the values in the keywords dictionary with the OR(|) operator updating
    # them in keywords_dict dictionary
    keywords_dict[intent]=re.compile(&#39;|&#39;.join(keys))

print(keywords)
print(keywords_dict)

# Building a dictionary of responses
responses={
    &#39;greet&#39; : &#39;Hello! How can I help you?&#39;,
    &#39;timings&#39;  : &#39;We are open from 9AM to 5PM, Monday to Friday. We are closed on weekends and public holidays&#39;,
    &#39;fallback&#39; : &#39;I dont quite understand. Could you repeat that?&#39;
}

print(&quot;Welcome to MyBank. How may I help you?&quot;)
# While loop to run the chatbot indefinetely
while True:
    # Takes the user input and converts all characters to lowercase
    user_input = input().lower()
    # Defining the Chatbot&#39;s exit condition
    if user_input == &#39;quit&#39;:
        print(&#39;Thank you for visiting.&#39;)
        break
    matched_intent = None
    for intent, pattern in keywords_dict.items():
        # Using the regular expression search function to look for keywords in user input
        if re.search(pattern, user_input):
            # if a keyword matches, select the corresponding intent from the keywords_dict dictionary
            matched_intent = intent
    # The fallback intent is selected by defualt
    key = &#39;fallback&#39;
    if matched_intent in responses:
        # If a keyword matches, the fallback intent is replaced by
        # the matched intent as the key for the responses dictionary
        key = matched_intent
    # The chatbot prints the response that matches the seleted intent
    print(responses[key])</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[알고리즘] 유전 알고리즘]]></title>
            <link>https://velog.io/@jmsuper_97/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%9C%A0%EC%A0%84-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98</link>
            <guid>https://velog.io/@jmsuper_97/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%9C%A0%EC%A0%84-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98</guid>
            <pubDate>Thu, 27 Oct 2022 05:09:41 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>참고자료</p>
</blockquote>
<ul>
<li><a href="http://wiki.hash.kr/index.php/%EC%9C%A0%EC%A0%84_%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98">http://wiki.hash.kr/index.php/유전_알고리즘</a></li>
</ul>
<br>

<h2 id="유전-알고리즘이란">유전 알고리즘이란?</h2>
<p>유전 알고리즘(Genetic Algorithm)은 1975년 존 홀랜드에 개발된 <strong>전역 최적화 기법(시간이 걸리더라도 전체 탐색영역에서 가장 좋은 해를 찾는 것)</strong>으로, 최적화 문제를 해결하는 기법의 하나이다. 자연 세계의 진화 과정에 기초한 계산 모델로서 생물의 진화를 모방한 진화 연산의 대표적인 기법이다. 실제 진화의 과정에서 많은 부분을 차용하였으며, 변이, 교배 연산 등이 존재한다.</p>
<p>유전 알고리즘은 어떤 미지의 함수 Y = f(x)를 최적화하는 해 x를 찾기 위해, 진화를 모방한(Simulated evolution) 탐색 알고리즘이라고 말할 수 있다. 유전 알고리즘은 특정한 문제를 풀기 위한 알고리즘이라고 보다는 문제를 풀기 위한 접근방법에 가까우며, 유전 알고리즘에서 사용할 수 있는 형식으로 바꾸어 표현할 수 있는 모든 문제에 대해 적용할 수 있다.</p>
<br>

<h2 id="구성">구성</h2>
<h3 id="개념">개념</h3>
<ul>
<li>염색체(chromosome) : 생물학적으로는 유전 물질을 담고 있는 하나의 집합을 의미하며, 유전 알고리즘에는 하나의 해(solution)를 표현한다. 어떠한 문제의 해를 염색체로 인코딩(encoding)한다.</li>
<li>유전자(gene) : 염색체를 구성하는 요소로써, 하나의 유전 정보를 나타낸다. 어떠한 염색체가 [A B C]라면, 이 염색체에는 각각 A, B 그리고 C의 값을 갖는 3개의 gene이 존재한다.</li>
<li>자손(offspring) : 특정 시간 t에 존재했던 염색체들로부터 생성된 염색체를 t에 존재했던 염색체들의 자손이라고 한다. 자손은 이전 세대와 비슷한 유전 정보를 갖는다.</li>
<li>적합도(fitness) : 어떠한 염색체가 갖고 있는 고유값으로써, 해당 문제에 대해 염색체가 표현하는 해가 얼마나 적합한지를 나타낸다.</li>
<li>교차율 또는 교배(crossover) : 2개의 염색체를 조합하여 새로운 염색체를 생성한다. 보통 교차율은 0.7(70%)로 정의한다.</li>
<li>돌연변이(mutation) : 생물학적 돌연변이와 같은 것으로 특정 확률로 염색체 또는 유전자를 변형한다. 변이 없이 알고리즘을 진행하다보면 여러 세대가 지나고 같은 데이터를 가진 염색체만 남는 경우가 있다. 변이를 통해 새로운 데이터에 대해 가능성을 열어둔다. 보통 변이율은 0.001(0.1%)로 정의한다.</li>
</ul>
<br>

<h3 id="유전-알고리즘을-활용하기-위한-요구-조건">유전 알고리즘을 활용하기 위한 요구 조건</h3>
<ul>
<li>해를 유전자(gene)의 형식으로 표현할 수 있어야 한다.</li>
<li>해가 얼마나 적합한지를 적합도 함수를 통해 계산할 수 있어야 한다.<ul>
<li>적합도 함수 : 해가 얼마나 문제의 답으로 적합한지를 평가하기 위한 함수</li>
</ul>
</li>
</ul>
<br>

<h3 id="흐름">흐름</h3>
<p>어떤 문제에 대해 유전자 형식이 정의되었다면, 어떤 해들의 유전자들을 서로 조합함으로써 기존의 해로부터 새로운 해를 만들어낼 수 있다. 이런 조합 연산은 교배(Crossover)에 비유할 수 있다.</p>
<p> 우수한 해들을 선택하여 이들을 교배하면, 만들어진 해는 우수한 해들이 가지는 특성을 물려받을 가능성이 높게 된다. 우수한 해의 선택에는 앞에서 정의한 적합도 함수를 이용할 수 있으며, 적합도가 높은 해가 선택될 확률을 높게 만들면, 보다 나은 유전자를 가진 해가 다음 세대에 자신의 유전자를 넘겨줄 확률이 높게 되고, 따라서 다음 세대의 해들은 최적해에 점차 가까워지게 된다. 또 비록 교배를 통해 후손을 남기지 못하더라도, 변이를 통해 새로운 유전자를 형성하여 다음 세대로 넘겨 주도록 할 수 있으며, 이는 지역 최적점에 빠지지 않도록 하는 주요한 기법이다.</p>
<p> 해들을 교배하기 위해서는 아담과 하와처럼 초기 해의 집단이 필요하다. 초기 해 집단은 단지 이후의 해를 구하는 데 있어 필요한 초기 개체로서의 역할만을 위한 것이므로, 우수한 해들로 이루어질 필요는 없다. 일반적으로는 유전자를 랜덤하게 생성하여 초기 해 집단을 구성한다.</p>
<p> 초기 해 집단이 구성되면, 이들 내부의 해의 교배를 통해 다음 세대의 해의 집합을 생성하게 되며, 이를 세대를 거듭하면서 반복해 가면, 해들은 점점 정답에 가까워지게 된다. 유전 알고리즘이 전역 최적해를 구하려면, 많은 인구를 유지하면서 많은 세대를 내려갈 필요가 있다. 따라서, 대부분의 경우는 세대가 일정 수준 진행되었거나, 해가 특정 범위에 들게되면 알고리즘을 종료하게 된다.</p>
<br>

<h3 id="구조">구조</h3>
<p>유전 알고리즘은 t에 존재하는 염색체들의 집합으로부터 적합도가 가장 좋은 염색체를 선택하고, 선택된 해의 방향으로 검색을 반복하면서 최적해를 찾아가는 구조로 동작한다. 유전 알고리즘의 동작을 단계별로 표현하면 아래와 같다.</p>
<ol>
<li>초기 염색체의 집합 생성</li>
<li>초기 염색체들에 대한 적합도 계산</li>
<li>현재 염색체들로부터 자손들을 생성</li>
<li>생성된 자손들의 적합도 계산</li>
<li>종료 조건 판별<ol>
<li>종료 조건이 거짓인 경우, (3)으로 이동하여 반복</li>
<li>종료 조건이 참인 경우 ,알고리즘을 종료</li>
</ol>
</li>
</ol>
<br>

<h2 id="연산">연산</h2>
<p>유전 알고리즘은 <code>선택</code>, <code>교차</code>, <code>변이</code>, <code>대치</code> 등 몇가지 주요 연산으로 구성된다.</p>
<h3 id="선택">선택</h3>
<p> 한 세대에서 다음 세대로 전해지는 해의 후보가 되는 해들을 선택한다. 선택 방법에는 균등 비례 룰렛 휠 선택, 토너먼트 선택, 순위 기반 선택 등이 있다.</p>
<p> 해의 선택은 유전 알고리즘의 성능에 큰 영향을 미친다. 어떤 방법을 쓰느냐에 따라 최적해로 다가가는 속도가 더디게 되거나, 아니면 지역 최적해에 빠지는 경우가 생길 수도 있기 때문이다. 또는 우수한 해가 보유한 나쁜 인자가 전체 인구에 퍼지거나, 반대로 나쁜 해가 보유한 우수한 인자가 영구히 사장될 수도 있다. 따라서 어떤 해를 선택하는지는 유전 알고리즘의 성능을 위해서 중요한 요소라고 할 수 있다.</p>
<p>일반적으로는 가장 좋은 해의 순으로 선택될 확률을 높게 부여하는 방법이 많이 사용된다. 이는 반대로 말하면, 나쁜 해라 할지라도 그 해에 포함된 좋은 인자를 퍼뜨릴 기회를 준다고 볼 수 있다. 현 세대에서 가장 좋은 해라 할지라도 실제로는 지역 최적해에 가까운 해일 수도 있고, 반대로 좋지 않은 해라 할지라도 전역 최적해에는 더 가까울 수도 있기 때문이다.</p>
<p> 실제로 전역 최적해가 어디에 있는지는 알 수 없는 일이므로, 가능한 해들의 평균적인 적합도를 높여 가면서도 유전자의 다양성을 유지하는 것이 지역 최적해에 빠지는 것을 가능한 한 방지하는 방법이며, 이는 실세계의 생명체들 역시 유전적 다양성을 유지하는 종이 장기적인 생존 가능성이 높은 것과 비견할 수 있다.</p>
<h3 id="교차">교차</h3>
<p>생명체는 세대 내에서의 교배를 통해 다음 세대를 생성한다. 이와 마찬가지로 유전 알고리즘에서 자연 선택된 해들은 교배를 통하여 다음 세대의 해들을 생성하게 된다. 일반적으로 두 개의 해를 선택한 후 둘 간에 교배 연산을 수행하게 되며, 이를 통해 생성된 해는 각각의 부모 해의 교차 연산을 통해서 서로 겹치지 않는 위치의 유전인자를 받아 새로운 유전자를 구성하게 된다.</p>
<p> 실제 생명체의 염색체가 재조합되는 과정에서 부모 염색체의 일부분이 특정 위치를 기준으로 서로 바뀌어 결합되는 경우가 있는데, 이 현상을 교차라고 한다. 유전 알고리즘의 교차 연산은 이 현상을 본따, 부모 해의 유전자들을 서로 교차시켜서 자식해를 만들어낸다. 교차 연산의 실제 수행 방법은 비트 단위 교차에서부터 블록 단위 교차까지 다양한 방법으로 구현할 수 있으며, 교차를 한 번만 수행할지 전 영역에 대해서 교차시킬지 역시 결정할 수 있다.</p>
<p> 또한 모든 해에 대해 교차연산을 수행할 경우 우수한 해가 다른 해와의 교배를 통해서 우수성을 잃어버림에 따라 세대의 최고 우수해가 보존되지 않는 상황이 발생할 수 있다. <strong>이런 경우를 방지하기 위하여 교차를 확률적으로 수행하여 우수한 해가 변형되지 않고 그대로 다음 세대에 전해질 확률을 부여</strong>하는 방법도 사용된다.</p>
<h3 id="변이">변이</h3>
<p>일반 생명체에서, 유전자의 교배 뿐 아니라, 하나의 유전자가 직접적으로 변이를 일으켜서 주어진 환경에서 살아남을 확률 역시 존재한다. 변이 연산은 주어진 해의 유전자 내의 유전 인자의 순서 혹은 값이 임의로 변경되어 다른 해로 변형되는 연산이다.</p>
<p> 해의 유전자들을 가상의 공간에 맵핑할 경우 교배 연산은 부모해들 사이의 어떤 지점에 자식해를 생성하는 것이라고 볼 수 있다면, 변이 연산은 임의의 위치로 점프하는 것에 비견할 수 있다. 따라서 약간의 확률로 변이 연산을 수행시켜 주는 것은 전체 세대가 함께 지역 최적해에 빠져드는 경우를 방지하기 위한 주요한 기법이 될 수 있으며, 해집단의 다양성을 높여준다.</p>
<h3 id="대치">대치</h3>
<p>교차·변이 등을 거쳐서 만들어진 새로운 해를 해집단에 추가하고, 기존 해 중 열등한 해를 가려내서 제외시키는 연산이다. 가장 품질이 나쁜 해를 대치하는 방법, 새로운 해의 부모해 중에서 새로운 해와 가장 비슷한 해를 대치시키는 방법(해집단의 다양성을 유지하기 위함) 등이 있다.</p>
<br>

<h2 id="특징">특징</h2>
<p>대부분의 전통적 최적화 방법은 지금까지 발견된 가장 좋은 한 개의 해결 방법을 가지고 탐색해 나가는 데에 비하여, 유전자 알고리즘은 후보 해결 방법들의 모집단을 갖고 탐색해 나간다.</p>
<p> 물론 현재의 모집단에서 가장 좋은 해결 방법은 오직 한 개 혹은 동등한 목적 함수 값을 갖는 몇 개일 것이다. 그러나 다른 해결 방법들은 현재는 좋은 방법이 아니지만 검색공간의 다른 영역에 있는 샘플 포인트로서 향후 그 영역에서 보다 나은 해결 방법을 찾을 수 있도록 하는 거점 역할을 한다. 즉, 해결 방법의 모집단을 사용함으로써 유전자 알고리즘이 로컬 최적화(localoptima)에 빠지는 위험을 방지할 수 있다.</p>
<h3 id="장단점">장단점</h3>
<ul>
<li>장점<ul>
<li>기울기 정보를 필요로 하지 않아, 함수에 연속이거나 미분가능성 등의 제약을 받지 않아 <strong>연속적 변수와 불연속적 변수들이 함께 섞여 있는 문제를 해결하는 데 적합</strong>하다.</li>
<li>로컬 최적화를 피해갈 수 있는 전반적인 매커니즘을 가지고 있다.</li>
<li>복수의 개체 사이에서 선택이나 교배 등의 유전적 조작에 의해, 상호 협력적으로 해의 탐색을 수행하고 있다. 따라서, 단순한 병렬적 해의 탐색과 비교하여 보다 좋은 해를 발견하기 쉽다.</li>
<li>인공 신경망(Neural Network), 특히 역전파 알고리즘 등에서는 평가 함수의 미분값을 필요로 하였다. 유전자 알고리즘에서는 현재 적응도를 분별할 수 있으면 되기 때문에 알고리즘이 단순하고, 평가 함수가 불연속인 경우에도 적용이 가능하다.</li>
</ul>
</li>
<li>단점<ul>
<li>유전자 알고리즘에서 도출한 최적 방법은 다른 방법에 비교하여 좋다는 의미일 뿐, 실제로 ‘최적’의 해결 방법이라는 개념은 포함하지 않는다. 즉, 어떤 도출된 해결 방법이 최적임을 검증할 방법이 없다.</li>
<li>현실 세계에 존재하는 문제 중 대상으로 하는 문제를 유전자형으로 바꾸는 과정이 간단하지 않다.</li>
<li>개체 수, 선택 방법, 교차의 결정, 돌연변이 비율, 최적화 함수 등 결정해 줘야 하는 변수가 많고, 그에 따른 결과 차이가 크다.</li>
</ul>
</li>
</ul>
<br>

<h2 id="단계">단계</h2>
<p>대부분의 유전자 알고리즘은 모집단을 갖고 그 모집단에서 먼저 n개의 해를 생성한다. 그리고 그 n개의 해에서 선택(selection), 교차(crossover), 변이(mutation)의 단계를 거쳐서 새로운 k개의 해를 생성하게 된다. 이렇게 만들어진 k개의 해는 기존에 있던 모집단에서 k개의 해를 대체해서 새로운 해가 된다. 이런 과정이 반복 조건을 만족할 때까지 계속 되고 모집단에 남은 해 중 가장 적합한 해가 최적해가 된다. 위와 같은 과정을 단계별로 살피면 다음과 같다.</p>
<h3 id="유전자형-결정">유전자형 결정</h3>
<ul>
<li>스키마 : 문제를 유전자형으로 표현하기 위해서는 우선 스키마(scheme)의 개념을 알아야 한다. 스키마는 염색체에서 나타날 수 있는 패턴을 말한다. 예를 들어, 염색체가 모두 3자리의 ‘100’으로 이뤄졌다고 했을 때, 이 염색체는 100,1<em>,</em>0,10,1<em>0,</em>00,***의 8개의 부분 패턴을 포함하고 있어, 8개의 스키마를 가지고 있는 것이다. 이진수로 표현된 염색체의 경우, 자리수가 n이라고 할 때 모두 2^n개의 스키마를 갖는다.</li>
<li>이진수 표현과 n진수 표현 : 문제의 해가 될 수 있는 것을 유전자형으로 표현하는 방법은 여러가지가 있는데, 존 홀랜드는 문제의 해를 유전자형으로 표현하기 위해 이진수(binary) 표현을 사용하였다. 이진수 표현 방법이 스키마 처리에 용이하단 이유에서이다. 그러나 이진수 표현 방법 외에 n진수(n-nary)로 문제를 해결하는 방법도 있다. 이진수로 표현할 경우, 교차할 때 추가변이 효과가 생겨 교차의 다양성을 기대할 수 있다. 또한 n진수로 표현할 경우는 의미 있는 스키마를 보존할 가능성이 높아진다.</li>
<li>실수 표현 : 유전자를 한 실수로 지정하는 실수 표현 방법으로도 유전자형 변환이 가능하다. 실수 표현은 독일의 진화 전략(Evolution Strategy)에 의해 발전했다. 실수 표현은 유전자로 변환하고자 하는 인자가 실수일 경우, 그대로 유전자형으로 가져다 쓸 수 있어 매우 간편하다.</li>
<li>그 밖의 표현 : 위의 두 방법 이외에도 유전자들의 현재 위치가 의미를 갖는 위치 기반 표현, 현재 위치는 아무 의미도 없고 상대적인 순서들로 표현되는 순서 기반 표현과 유전자를 1차원으로 표현했을 때 생기는 정보 손실을 막기 위한 다차원 표현 방법 등 여러가지 표현 방법이 있다.</li>
</ul>
<h3 id="초기-모집단-결정">초기 모집단 결정</h3>
<p> 유전자 알고리즘에서 모집단은 염색체 간의 상호작용을 위해 매우 중요하다. 모집단의 크기는 초반에 지정해아 한다. 만약 모집단의 크기가 너무 작을 경우 염색체의 숫자도 적어져, 교차를 통한 진화를 목적으로 유전자 알고리즘의 장점을 제대로 활용할 수 없다.</p>
<p> 모집단의 크기가 너무 크다면 한 세대에 너무 많은 연산을 필요로 하기 때문에 유전자 알고리즘의 속도가 저하될 수 있다. 유전자형 표현과 마찬가지로 설계자의 경험에 의한 적절한 모집단 크기 결정이 필요하다.</p>
<h3 id="개체-적합도">개체 적합도</h3>
<p> 계산초기 모집단에 존재하는 유전자들은 우리가 찾고자 하는 해에 가깝기만 하고 해가 될 수는 없는 부족한 유전자일수도 있다. 초기 세대에 이런 유전자를 이용해서 알고리즘을 진행하면 그 다음 세대까지 영향을 받게 되어서, 우리가 찾고자 하는 다른 적합도 높은 유전자를 발견하지 못할 수도 있다.</p>
<p> 이것을 막기 위해 각 개체의 적합도를 평가하기 위해서는 평가함수를 구해야 한다. 그런데 구하고자 하는 문제에 따라서 평가 함수의 구간값이 다르기 때문에, 구간 사이의 표준화된 값을 정해서 새로운 적합도 함수를 구해 이것을 개체 선택에 이용해야한다. 원래의 구간값을 새로운 표준화된 값으로 바꾸는 방법은 여러 가지가 있지만, 다음이 대표적인 방법들이다.</p>
<pre><code class="language-java">f : 표준화 전의 적합도 값
f&#39; : 표준화 후의 적합도 값</code></pre>
<ul>
<li>선형 표준화 : <code>f’ : af + b</code> 의 식으로 실행하며, 선형표준화에서 적합도 값이 음수가 되지 않도록 수를 조정해 줘야 한다.</li>
<li>거듭제곱 표준화 : <code>f’ : f^k</code> 으로 실행하며, k값은 문제에 따라 가변적이다.</li>
<li>a 절단 : <code>f’ = f - (m - a)</code> 의 식에 의해 시행되며, 시그마 절단 방법에서 표준화후의 적합도가 음수가 되는 경우에는 이를 0으로 바꿔줘야 한다. 모집단의 각 개체의 적합도를 검사하며, 적합도 검사 결과 해(solution)로서 만족하는 개체가 있으면 알고리즘이 종료되지만 만족하는 개체가 없을 경우에는 다음 단계로 넘어간다.</li>
</ul>
<h3 id="선택-수행">선택 수행</h3>
<p>선택 과정에서는 우수 유전자가 생존에 유리한 자연 선택과정과 유사하게 진행되는데, 여러가지 선택 방법들의 적합도 함수에 의해 계산된 유전자 중, 해에 가까운 유전자들에게 좀 더 높은 선택 확률을 준다는 공통점을 가지고 있다.</p>
<ul>
<li><strong>룰렛 선택</strong> : 룰렛 선택은 적합도 비례 방법이라고도 한다. 각 유전자의 적합도에 따라 선택될 확률을 k배로 조정하는 방법이다. 룰렛 방법은 각각의 유전자가 룰렛 위의 적합도만큼의 공간을 차지하고 있다고 가정한다. 그 후에 화살을 쏘았을 때 선택 확률은 룰렛 위에 유전자가 차지하고 있는 공간에 비례하게 된다. 확률에 기반하였기 때문에 우수한 개체라도 선택되지 않을 가능성이 있으며 열등한 개체라도 선택될 가능성이 있다. 그러나 모집단 내의 개체수가 적을 경우, 선택되는 개체의 분포에 다양하지 않다는 한계가 있다.</li>
<li><strong>기대값 선택</strong> : 적합도에 대한 각 개체의 기대값을 구하고 그 값에 따라 확률적인 재생 개수를 구하여 선택하는 방법이다. 기대값 선택의 경우, 한 번 선택된 개체가 반복해서 선택될 가능성이 있기 때문에 한 번 선택된 개체수는 기대값을 곱해 줘서 다시 뽑힐 확률을 최소화시킨다.</li>
<li><strong>순위 기반 선택</strong> : 룰렛 선택 방법은 확률에 따라 개체의 선택여부가 결정되기 때문에 우수한 개체가 많이 선택된다는 장점도 있지만 특정 개체들만 선택될 가능성도 있다. 순위 기반 선택 방법은 그걸 보완하여 적합도에 따라 순위를 매기고, 그 순위에 따라 확률을 결정하는 방법이다.</li>
<li><strong>토너먼트 선택</strong> : 개체군 중에서의 임의의 개체들을 선택해서 비교한 다음에, 그 중에 적합도가 더 높은 개체를 다음 세대에 남기는 선택 방법이다. 그렇게 선택된 개체를 또 다른 개체와 계속 비교한 다음에 그 중에서 적합도가 높은 개체를 남기게 되는 방식이 토너먼트 방식과 유사하여 토너먼트 선택 방법이라 한다.</li>
<li><strong>엘리트 보존 방법</strong> : 확률에 따라 개체를 선택하게 될 때, 우수한 개체가 소실되는 것을 막기 위해 가장 좋은 해를 보존하여 다음 세대에 넘기는 선택 방법이다. 보통 다른 방법과 함께 사용된다.</li>
</ul>
<h3 id="교차-처리">교차 처리</h3>
<p>부모 염색체를 부분적으로 바꾸어 자식의 염색체를 생성하는 방법</p>
<ul>
<li><p>일점 교차</p>
<p>  교차하는 위치를 한 군데 결정하고, 그 앞과 뒤에서 어느 쪽의 유전자형을 받을 것인가를 결정하여 변경시키는 방법으로 가장 일반적인 교차 방법이다.</p>
<pre><code class="language-java">  P1: a b c d e f g h i j 
  P2: k l m n 0 ↓ p q r s t 
  S1: a b c d e p q r s t 
  S2: k l m n o f g h i j</code></pre>
</li>
<li><p>복수점 교차</p>
<p>  교차 위치가 복수인 방식이다. 예를 들어 교차 위치가 2와 5라면 새로운 개체의 하나는 개체 A의 선두로부터 두 번째까지, 개체 A의 세 번째로부터 다섯 번째까지, 개체 의 여섯 번째부터 마지막까지 유전자가 만들어진다. 동시에 그 반드의 조합에 의해서 또 다른 하나의 새로운 개체의 유전자가 만들어진다.</p>
<pre><code class="language-java">  P1: a b c d e f g h i j 
  P2: k l m n o p q r s t 
  S1: a b c n o p q r s t 
  S2: k l m d e p q h i j</code></pre>
</li>
<li><p>균일 교차</p>
<p>  위의 방법과 같이 지름선을 사용하지 않고, 특정한 마스크를 사용하여 P1과 P2에서 가져오는 유전자를 결정하는 방식이다. 마스크의 값이 0일 경우는 P1에서 가져오고, 1일 경우는 P2에서 값을 가져온다. 일점 교차나 복수점 교차에 비해 스키마의 결합 형태가 다양하고, 그만큼 수렴 시간이 오래 걸린다.</p>
<pre><code class="language-java">  P1: a b c d e f g h i j 
  P2: k l m n o p q r s t
  M0: 0 1 1 0 1 0 1 1 0 0 
  S1: a l m d o f q r i j 
  S2: k b c n e p g h s t</code></pre>
</li>
<li><p>변이 수행</p>
<p>  변이는 유전자를 일정한 확률로 변화시키는 조작이다. 돌연변이를 너무 큰 변이 확률로 설정하면 적합도가 떨어지는 경우도 생기게 되지만 오히려 적합도가 높아지는 경우도 생긴다.</p>
<p>   초기에는 품질이 좋지 않은 해가 많으므로 변이의 강도를 높이는 게 좋으며, 후반에 가서는 품질 향상에 변이가 큰 영향을 미치지 않으므로 강도를 낮춰 준다. 변이는 개체군의 다양성을 유지하여 국지 최적해(localminimum)에 빠질 위험을 배제한다. 그러나 변이의 확률이 높아지면 수렴성이 떨어져 수행 시간이 많이 걸리게 된다.</p>
</li>
</ul>
<h2 id="유전-알고리즘을-활용한-tsp">유전 알고리즘을 활용한 TSP</h2>
<h3 id="with-완전탐색">With 완전탐색</h3>
<pre><code class="language-python">import pandas as pd
from itertools import permutations

def solution(dist_table):
    print(len(dist_table),len(dist_table[0]))
    temp = [i for i in range(9)]
    min_val = 999999999
    min_subject = []
    subject_iter = permutations(temp) # len : 362880
    for subject in subject_iter:
        val = 0
        prev_node = subject[0]
        for i in range(1,9):
            node = subject[i]
            val += int(dist_table[prev_node][node])
            prev_node = node
        val += int(dist_table[subject[8]][subject[0]])
        if val &lt; min_val:
            min_val = val
            min_subject = subject
    print(&quot;최단 경로 : &quot; + str(min_subject))
    print(&quot;최단 거리 : &quot; + str(min_val))
    # 최단 경로 : (0, 1, 2, 8, 7, 6, 5, 4, 3)
    # 최단 거리 : 1018

def main():
    df = pd.read_excel(&#39;distance.xlsx&#39;,header=1,usecols=range(1,10))
    dist_table = df.values.tolist()
    solution(dist_table)

if __name__ == &quot;__main__&quot;:
    main()
</code></pre>
<h4 id="출력값">출력값</h4>
<p><img src="https://velog.velcdn.com/images/jmsuper_97/post/c96c169b-63a1-4e6b-ae80-92c461e5dca7/image.png" alt=""></p>
<h3 id="with-유전-알고리즘">With 유전 알고리즘</h3>
<pre><code class="language-python">import random
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
from statsmodels.formula.api import ols

POPULATION_SIZE = 16    # 개체 집단의 크기
MUTATION_RATE = 0.2    # 돌연 변이 확률
SIZE = 9            # 하나의 염색체에서 유전자 개수        
df = pd.read_excel(&#39;distance.xlsx&#39;,header=1,usecols=range(1,10))
dist_table = df.values.tolist()
TARGET_VAL = 1018

# 최단 경로 : (0, 1, 2, 8, 7, 6, 5, 4, 3)
# 최단 거리 : 1018

class Chromosome:
    def __init__(self, g = []):
        self.genes = g        
        self.fitness = 0        
        if self.genes.__len__()==0:    
            temp_list = list(range(9))
            random.shuffle(temp_list)
            self.genes = temp_list.copy()

    def cal_fitness(self):        # 적합도를 계산
        global dist_table
        self.fitness = 0
        value = 0

        prev_node = self.genes[0]
        for i in range(1,9):
            node = self.genes[i]
            value += int(dist_table[prev_node][node])
            prev_node = node
        value += int(dist_table[self.genes[8]][self.genes[0]])

        self.fitness = TARGET_VAL * 2 - value if TARGET_VAL * 2 - value &gt; 0 else 1
        return self.fitness

    def __str__(self):
        return self.genes.__str__()

def print_p(pop):
    i = 0
    for x in pop:
        print(&quot;염색체 #&quot;, i, &quot;=&quot;, x, &quot;적합도=&quot;, x.fitness)
        i += 1
    print(&quot;&quot;)

# 선택 연산
def select(pop):
    max_value  = sum([c.cal_fitness() for c in population])
    pick    = random.uniform(0, max_value)
    current = 0

    for c in pop:
        current += c.cal_fitness()
        if current &gt; pick:
            return c

# 교차 연산
def crossover(pop):
    father = select(pop)
    mother = select(pop)
    length = random.randint(1, SIZE - 1)
    idx = random.randint(0, SIZE - length)

    t_child1 = mother.genes[idx:idx + length].copy()
    t_child2 = father.genes[idx:idx + length].copy()

    child1 = list(filter(lambda x: not x in t_child1,father.genes))
    child2 = list(filter(lambda x: not x in t_child2,mother.genes))

    child1 = child1[:idx] + t_child1 + child1[idx:]
    child2 = child2[:idx] + t_child2 + child2[idx:]

    return (child1, child2)

# 돌연변이 연산
def mutate(c):
    if random.random() &lt; MUTATION_RATE:
        x, y = random.sample(list(range(0,9)),2)
        c.genes[y], c.genes[x] = c.genes[x], c.genes[y]

# 메인 프로그램
population = []
i=0
fitness_list = []

# 초기 염색체를 생성하여 객체 집단에 추가한다. 
while i&lt;POPULATION_SIZE:
    population.append(Chromosome())
    i += 1

count=0
population.sort(key=lambda x: x.cal_fitness(),reverse=True)
print(&quot;세대 번호=&quot;, count)
print_p(population)
count=1

max_fitness = 0

while population[0].fitness &lt; TARGET_VAL:
    if population[0].fitness &gt; max_fitness:
        MUTATION_RATE = MUTATION_RATE * 0.9
        max_fitness = population[0].fitness
    new_pop = []

    # 선택과 교차 연산
    for _ in range(POPULATION_SIZE//2):
        c1, c2 = crossover(population)
        new_pop.append(Chromosome(c1))
        new_pop.append(Chromosome(c2))

    # 자식 세대가 부모 세대를 대체한다. 
    # 깊은 복사를 수행한다. 
    population = new_pop.copy()

    # 돌연변이 연산
    for c in population: mutate(c)

    # 출력을 위한 정렬
    population.sort(key=lambda x: x.cal_fitness(),reverse=True)
    fitness_list.append(population[0].fitness)
    print(&quot;세대 번호=&quot;, count)
    print_p(population)
    count += 1
    if count &gt; 50000 : break

x, y = range(len(fitness_list)),fitness_list
fit_line = np.polyfit(x,y,1)
x_minmax = np.array([min(x), max(x)])
fit_y = x_minmax * fit_line[0] + fit_line[1]

plt.plot(fitness_list,label=&#39;fitness&#39;)
plt.plot(x_minmax, fit_y, color = &#39;red&#39;,label=&#39;regression&#39;)
plt.xlabel(&#39;generation number&#39;)
plt.ylabel(&#39;fitness&#39;)
plt.legend()
plt.show()</code></pre>
<h4 id="출력값-1">출력값</h4>
<p><img src="https://velog.velcdn.com/images/jmsuper_97/post/91d23f4f-d00e-4216-9d16-5e3050dac824/image.JPG" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[블록체인] 섹션1 : 블록체인 상태와 트랜잭션]]></title>
            <link>https://velog.io/@jmsuper_97/%EB%B8%94%EB%A1%9D%EC%B2%B4%EC%9D%B8-%EC%84%B9%EC%85%981-%EB%B8%94%EB%A1%9D%EC%B2%B4%EC%9D%B8-%EC%83%81%ED%83%9C%EC%99%80-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98</link>
            <guid>https://velog.io/@jmsuper_97/%EB%B8%94%EB%A1%9D%EC%B2%B4%EC%9D%B8-%EC%84%B9%EC%85%981-%EB%B8%94%EB%A1%9D%EC%B2%B4%EC%9D%B8-%EC%83%81%ED%83%9C%EC%99%80-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98</guid>
            <pubDate>Tue, 25 Oct 2022 14:16:45 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>이 글은 &#39;한양대 x 그라운드X&#39;에서 진행한 블록체인 강의 영상을 정리한 것입니다.
참고링크 : <a href="https://www.youtube.com/watch?v=imh8z8Mf764&amp;list=PLKqrwxupttYEcJhWAw0E_5RVpDD9LD6Q-&amp;index=8">https://www.youtube.com/watch?v=imh8z8Mf764&amp;list=PLKqrwxupttYEcJhWAw0E_5RVpDD9LD6Q-&amp;index=8</a></p>
</blockquote>
<br>

<h2 id="블록체인의-상태">블록체인의 상태</h2>
<h3 id="어카운트-기반-블록체인의-상태">(어카운트 기반) 블록체인의 상태</h3>
<p>블록체인은 트랜잭션으로 변화하는 상태 기계 (State Machine)</p>
<p><img src="https://velog.velcdn.com/images/jmsuper_97/post/baa5c01c-6a03-489f-a49f-71353bf534f3/image.png" alt=""></p>
<h3 id="상태-기계">상태 기계</h3>
<ul>
<li>블록체인은 초기 상태에서 변경사항을 적용하여 최종 상태로 변화하는 상태 기계<ul>
<li>이전 블록의 최종 상태(final state)는 현재 블록의 초기 상태(initial state)</li>
<li>Genesis block의 경우 임의의 초기값들이 설정되는데 이것이 genesis block의 초기상태이자 최종상태</li>
</ul>
</li>
<li>(어카운트 기반) 블록체인의 상태<ul>
<li>TX는 어카운트를 생성하거나 변경<ul>
<li>e.g., Alice가 기존에 존재하지 않던 주소 X에 1 ETH를 전송하면 새로운 EOA가 생성</li>
<li>e.g., Alice가 새로운 스마트 컨트랙트를 배포 (컨트랙트로 어카운트)</li>
<li>e.g., Alice가 Bob에게 5 ETH를 전송하는 TX가 체결되면 Alice의 Bob의 잔고가 변경</li>
</ul>
</li>
<li>항상 같은 결과를 보장하기 위해 하나의 TX가 반영되는 과정에서 다른 TX의 개입은 제한됨</li>
</ul>
</li>
</ul>
<p><br><br></p>
<h2 id="트랜잭션">트랜잭션</h2>
<h3 id="ethereum-어카운트의-종류">Ethereum 어카운트의 종류</h3>
<ol>
<li>External Account : 사용자(end user)가 사용하는 어카운트(a.k.a. EOA)</li>
<li>Contract Account : 스마트 컨트랙트를 표현하는 어카운트</li>
</ol>
<ul>
<li><p>Ethereum은 EOA와 스마트 컨트랙트의 상태를 기록 및 유지</p>
<ul>
<li>스마트 컨트랙트는 특정주소에 존재하는 실행 가능한 프로그램</li>
<li>프로그램은 상태를 가지기 때문에 Ethereum/Klayton은 스마트 컨트랙트를 어카운트로 표현</li>
</ul>
</li>
<li><p>EOA는 블록에 기록되는 TX를 생성</p>
<ul>
<li><p>블록에 기록되는 TX들은 명시적인 변경을 일으킴</p>
<p>  (e.g., 토큰 전송, 스마트 컨트랙트 배포/실행)</p>
</li>
</ul>
</li>
<li><p>EOA와 CA의 차이점은 EOA만 트랜잭션을 생성할 수 있다는 점이다. CA는 passive한 존재이다. 항상 누군가가 실행시켜줘야 한다.</p>
</li>
</ul>
<h3 id="트랜잭션tx과-가스gas">트랜잭션(TX)과 가스(Gas)</h3>
<ul>
<li>TX의 목적은 블록체인의 상태를 변경하는 것<ul>
<li>TX는 보내는 사람(sender, from)과 받는 사람(recipient, to)이 지정되어 있으며</li>
<li>to가 누구냐에 따라 TX의 목적이 세분화</li>
</ul>
</li>
<li>Gas: TX를 처리하는데 발생하는 비용<ul>
<li>TX를 처리하는데 필요한 자원(computing power, storage)을 비용으로 전환한 것이 가스(gas)</li>
<li>Sender는 TX의 처리를 위해 필요한 가스의 총량과 같은 가치의 플랫폼 토큰을 제공해야함</li>
<li>이때 지출되는 플랫폼 토큰을 가스비(Gas Fee)라 정의</li>
<li>가스비는 블록을 생성한 노드가 수집</li>
</ul>
</li>
</ul>
<p><br><br></p>
<h2 id="트랜잭션과-서명">트랜잭션과 서명</h2>
<ul>
<li>플랫폼은 sender가 TX를 처리하는데 필요한 가스비를 가지고 있는지 확인<ul>
<li>가스비 확인은 구현에 따라 상이</li>
<li>Ethereum/Klayton은 노드가 TX를 수신함과 동시에 가스비 이상의 balance가 있는지 확인</li>
<li>TX의 체결과 동시에 sender의 balance에서 가스비를 차감</li>
</ul>
</li>
<li>TX는 sender의 서명(v, r, s)이 필요<ul>
<li>어카운트의 balance를 사용하기 때문</li>
<li>서명의 증명은 구현마다 상이<ul>
<li>Ethereum: 서명 → 공개키 도출 → 어카운트 주소 도출 → 어카운트 존재유무 확인</li>
<li>Klayton: from 주소 확인 → 저장된 공개키 불러오기 → 서명 직접 검증</li>
</ul>
</li>
</ul>
</li>
</ul>
<p><br><br></p>
<h2 id="트랜잭션-예시">트랜잭션 예시</h2>
<p>{</p>
<p>nonce: 1, (nonce는 account가 몇 번째 트랜잭션을 보내는지를 말해주는 것)</p>
<p>from : <del>~</del></p>
<p>to: <del>~</del></p>
<p>value: 10,</p>
<p>// omitted other fields for brevity</p>
<p>// platform specific fields are requried</p>
<p>}</p>
<h3 id="ethereum-트랜잭션-예시">Ethereum 트랜잭션 예시</h3>
<p>{</p>
<p>nonce: ‘0x01’,</p>
<p>gasPrice: ‘0x4a817c800’, (gas 1개의 가격)</p>
<p>gas: ‘0x5208’, (지불할 의향이 있는 gas의 개수)</p>
<p>value: ‘0xde0bdc000’</p>
<p>to: <del>~</del></p>
<p>v: ‘0x25’, (식별자)</p>
<p>r: <del>~</del> (서명) </p>
<p>s: <del>~</del> (서명)</p>
<p>}</p>
<p>(v, r, s)는 서명을 의미한다. 엄밀히 말하면 (r, s)가 서명을 의미한다.</p>
<p><br><br></p>
<h2 id="transaction-journey">Transaction Journey</h2>
<p><img src="https://velog.velcdn.com/images/jmsuper_97/post/9cba8830-8c25-4b26-9bdf-4db8e8749704/image.png" alt=""></p>
<ol>
<li>Alice는 트랜잭션을 생성해서 특정 노드에게 전달</li>
<li>노드는 블록을 생성하기 위해 노력하고, 생성되어 트랜잭션이 체결</li>
<li>체결되었다는 Receipt를 Alice에게 전달</li>
</ol>
<h3 id="alice와-node-사이-통신">Alice와 Node 사이 통신</h3>
<p><strong>Alice → Node</strong></p>
<ul>
<li>Alice는 TX를 생성, 서명하여 Node에게 전달</li>
<li>이때 데이터 구조를 온전하게 전달하고자 RLP 알고리즘으로 TX를 직렬화</li>
<li>Alice와 Node가 <strong>같은 프로토콜로 통신하는 것이 중요</strong></li>
</ul>
<p>Node → Alice</p>
<ul>
<li>올바른 TX 수신 시 TX 해시를 반환</li>
<li>TX 채결 시 Receipt를 반환; 소요된 Gas, TX 해시, input 등이 기록</li>
</ul>
<p><br><br></p>
<h2 id="스마트-컨트랙트와-솔리디티">스마트 컨트랙트와 솔리디티</h2>
<h3 id="smart-contract">Smart Contract</h3>
<ul>
<li>특정 주소에 배포되어 있는 TX로 실행 가능한 코드<ul>
<li>스마트 컨트랙트 소스코드는 함수와 상태를 표현; 컨트랙트 소스코드는 블록체인에 저장</li>
<li>함수는 상태를 변경하는 함수, 상태를 변경하지 않는 함수로 분류</li>
<li>사용자(end user, EOA owner)가 스마트 컨트랙트 함수를 실행하거나 상태를 읽을 때 주소가 필요</li>
</ul>
</li>
<li>스마트 컨트랙트는 사용자가 실행<ul>
<li>상태를 변경하는 함수를 실행하려면 그에 맞는 TX를 생성하여 블록에 추가(TX 체결 = 함수의 실행)</li>
<li>상태를 변경하지 않는 함수, 상태를 읽는 행위는 TX가 필요 없음(노드에서 실행)</li>
</ul>
</li>
</ul>
<h3 id="solidity">Solidity</h3>
<ul>
<li>Ethereum/Klaytn에서 지원하는 스마트 컨트랙트 언어</li>
<li>Klaytn은 Solidity 버전 0.4.24, 0.5.6을 지원</li>
<li>일반적인 프로그래밍 언어와 그 문법과 사용이 유사하나 몇가지 제약이 존재(e.g., 포인터의 개념이 없기 때문에 recursive type의 선언이 불가능)</li>
</ul>
<h3 id="contract--code--data">Contract = Code + Data</h3>
<ul>
<li>Solidity 컨트랙트는 코드(함수)와 데이터(상태)로 구성</li>
<li>Solidity 함수는 코드 안에 변수로 선언된 상태를 변경하거나 불러옴</li>
<li>아래 예시에서 set, get은 함수, storedData는 상태</li>
</ul>
<pre><code class="language-solidity">contract SimpleStorage{
    uint storedData;
    function set(uint x) public{
        storedData = x;
    }
    function get() public view returns (uint){
        return storedData;
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[블록체인] 섹션0 : 블록체인의 기본]]></title>
            <link>https://velog.io/@jmsuper_97/%EB%B8%94%EB%A1%9D%EC%B2%B4%EC%9D%B8-%EC%84%B9%EC%85%980-%EB%B8%94%EB%A1%9D%EC%B2%B4%EC%9D%B8%EC%9D%98-%EA%B8%B0%EB%B3%B8</link>
            <guid>https://velog.io/@jmsuper_97/%EB%B8%94%EB%A1%9D%EC%B2%B4%EC%9D%B8-%EC%84%B9%EC%85%980-%EB%B8%94%EB%A1%9D%EC%B2%B4%EC%9D%B8%EC%9D%98-%EA%B8%B0%EB%B3%B8</guid>
            <pubDate>Mon, 24 Oct 2022 07:41:55 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>이 글은 &#39;한양대 x 그라운드X&#39;에서 진행한 블록체인 강의 영상을 정리한 것입니다.
참고링크 : <a href="https://www.youtube.com/watch?v=imh8z8Mf764&amp;list=PLKqrwxupttYEcJhWAw0E_5RVpDD9LD6Q-&amp;index=8">https://www.youtube.com/watch?v=imh8z8Mf764&amp;list=PLKqrwxupttYEcJhWAw0E_5RVpDD9LD6Q-&amp;index=8</a></p>
</blockquote>
<br>

<h2 id="블록체인의-정의-해시함수">블록체인의 정의, 해시함수</h2>
<br>

<h3 id="블록체인이란">블록체인이란?</h3>
<p>정보를 블록이라고 하는 단위로 저장하여 저장된 블록들을 체인형태로 묶은 저장기술을 말한다. 자료구조의 연결리스트와 유사하다. 연결리스트에서는 특정 노드의 다음 노드가 누구인지 알기 위해 다음 노드의 주소값을 특정 노드가 가지고 있다. 그러나, 블록체인에서 블록은 해시 함수를 이용해 다음 블록이 어떤 블록인지 알아낸다.</p>
<p><img src="https://velog.velcdn.com/images/jmsuper_97/post/03d6cb39-b45c-4749-92a0-061298c51a3c/image.png" alt=""></p>
<h3 id="해시함수hash-function">해시함수(Hash Function)</h3>
<p>임의의 길이의 데이터를 고정된 길이의 데이터로 매핑하는 함수를 말한다. 다시말해, 데이터의 길이가 어떠하던간에 상관없이 고정된 길이의 데이터로 변환해준다. </p>
<p> 해시함수의 특징은 하나의 데이터에서 오직 단 하나의 해시(결과값)이 도출된다는 것이다. 임의의 데이터 X와 Y가 있을 때 아래 규칙을 만족한다.</p>
<ul>
<li><p>if X == Y  then H(X) == H(Y)</p>
</li>
<li><p>if X ≠ Y then H(X) ≠ H(Y)</p>
</li>
<li><p>if H(X) == H(Y) then X == Y</p>
<p>따라서, 특정 input value(입력값)은 고유한 output value(출력값)에 매핑된다. 이는 1대1 관계를 갖는다.</p>
</li>
</ul>
<h3 id="해시-함수의-예제">해시 함수의 예제</h3>
<ul>
<li><p>같은 함수로 다른 데이터를 해시했을 경우</p>
<ul>
<li><p>문자열 ‘hello!’를 SHA-256으로 해시한 결과는 다음과 같다:</p>
<p>  1C0B8448B72C55F2F614640252D14EC132FA031D7C5C35D93B6BCE780CFE0E92</p>
</li>
<li><p>문자열 ‘hello?’를 SHA-256으로 해시한 결과는 다음과 같다:</p>
<p>  772B10CBE98FA326726AF507FB4BE2B5B72939DC37E5D0FD05E4AD95DEC2905F</p>
</li>
</ul>
</li>
</ul>
<pre><code>같은 함수라도 다른 데이터를 해시할 경우 결과값이 크게 다른 것을 확인할 수 있다. 차이는 ! 와 ? 임에도 불구하고 값은 크게 달라진다.</code></pre><ul>
<li><p>다른 함수로 같은 데이터를 해시했을 경우</p>
<ul>
<li><p>문자열 ‘hello!’를 SHA-256으로 해시한 결과는 다음과 같다:</p>
<p>  1C0B8448B72C55F2F614640252D14EC132FA031D7C5C35D93B6BCE780CFE0E92</p>
</li>
<li><p>문자열 ‘hello!’를 Keccak-256으로 해시한 결과는 다음과 같다.</p>
<p>  96b8d442f4c09a08d266bf37b18219465cfb341c1b3ab9792a6103a93583fdf7</p>
</li>
</ul>
</li>
</ul>
<br>

<h2 id="블록체인의-구조-주요-용어">블록체인의 구조, 주요 용어</h2>
<br>

<h3 id="블록체인의-구조">블록체인의 구조</h3>
<p><img src="https://velog.velcdn.com/images/jmsuper_97/post/674d1fc4-741c-480c-adaf-c7a4b07bd0f5/image.png" alt=""></p>
<ul>
<li>블록 헤더 : 블록을 설명하는 정보와 이전 블록의 해시를 포함한다.<ul>
<li>이전 블록의 해시(hash pointer)를 가지고 있기 때문에 ‘어떤 블록이 앞에 와야하는지 결정적으로 알 수 있다’ &amp; ‘이를 바탕으로 블록의 순서를 결정할 수 있다’</li>
<li>뒤에 블록이 앞에 블록을 가리킨다라는 개념이 핵심이다.</li>
<li>헤더는 바디를 설명한다.</li>
<li>해시함수에 들어가는 입력값은 블록 바디에 담긴 정보이다. 거래 정보는 축적되면서 계속 쌓이기 때문에, 입력값으로 동일한 값이 들어가는 경우는 존재하지 않는다.</li>
</ul>
</li>
<li>블록 바디 : 정보의 묶음</li>
</ul>
<h3 id="블록높이-블록생성주기">블록높이, 블록생성주기</h3>
<p>블록체인에서 ‘블록높이’라는 개념은 ‘블록은 계속 쌓이며, 중간 블록이 소실되면 전체 블록체인이 흔들린다’라는 말에 의해 등장하였다.</p>
<p>이전 블록이 아래에, 최근 블록이 위에 오도록 블록들을 정렬하면, 블록이 생성됨에 따라 체인의 높이가 높아진다. 블록의 순서를 그 블록이 위치한 ‘높이’(block height)라 부른다. 첫번째 블록은 편의상 높이를 0이라 한다.</p>
<p>블록과 블록 사이에는 생성시간의 갭이 존재한다. 이 갭을 ‘블록생성주기’라고 부른다. 다만, 블록체인에서 정한 블록생성주기가 항상 일치하는 것은 아니고, 특정 기간에 수렴하도록 설정한다.</p>
<p>블록생성주기가 중요한 이유는 체결과 관련있기 때문이다. 블록이 생성되어 블록체인에 최종적으로 등록되는 것을 ‘체결’이라고 부른다. 만약 블록생성주기가 15분이라면, 체결되는데 15분 정도 소요되고, 1초라면, 체결되는데 1초 정도 소요된다.</p>
<p>궁금증 : 블록생성주기 동안에 특정 계좌에서 발생되는 거래는 오직 하나여야 할까? 여러 거래가 발생할 경우 계좌의 synchronization이 보장될까?</p>
<p>→ 해당 계좌에서 여러 트랜잭션이 발생했을 때 실제 블록에 기록되는 트랜잭션은 하나이다. 여러 트랜잭션이 발생했다고 하더라도, 가장 먼저 도착한 트랜잭션이 등록되게 된다. </p>
<br>

<h2 id="블록체인-네트워크">블록체인 네트워크</h2>
<p><img src="https://velog.velcdn.com/images/jmsuper_97/post/11657b62-560e-4a52-bc7d-8b3918569585/image.png" alt=""></p>
<p>블록체인은 기본적으로 P2P(Peer-to-Peer) 구조이다. 중앙 서버에 의존하고 있는 구조가 아니다. 네트워크 참여자 전원은 모든 블록을 동일한 순서로 저장하여 모두 같은 블록체인을 유지한다.</p>
<h3 id="합의-consensus">합의 (Consensus)</h3>
<p><img src="https://velog.velcdn.com/images/jmsuper_97/post/8362e0ad-4f3a-4092-a2d8-b2aa28d8a090/image.png" alt=""></p>
<ul>
<li>자격이 있는 참여자는 블록을 제안(propose)할 수 있음</li>
<li>블록 제안 자격은 네트워크마다 상이하다.(ex. PoW, PoS)</li>
<li>노드들은 제안자가 올바른 자격을 취득했는지, 제안된 블록이 올바른지 검증한 뒤 블록을 자신의 체인에 추가한다.</li>
<li>정족수 또는 정해진 기준을 만족하는 수의 노드가 블록을 자신의 체인에 추가하면 합의가 이뤄졌다고 판단한다.</li>
</ul>
<h3 id="정리--블록체인의-성질">정리 : 블록체인의 성질</h3>
<ul>
<li>블록체인은 한명  이상의 참여자가 있는 네트워크에서 관리</li>
<li>네트워크 참여자 전원은 모든 블록을 동일한 순서로 저장하여 모두 같은 블록체인을 유지</li>
<li>자격이 있는 참여자는 블록을 제안할 수 있음. 블록 제안 가격은 네트워크마다 상이</li>
<li>블록이 체인에 추가됨 ⇒ 참여자들이 새 블록을 자신의 체인에 추가</li>
<li>따라서 새로운 블록이 체인에 추가되려면 네트워크의 합의가 필요. 합의방법은 네트워크마다 상이<ul>
<li>어느 한 주체가 단독으로 결정하는 구조가 아닌, 여러 참여자가 합의를 통해 결정하기 때문에 블록체인은 탈중앙화되어 있다고 표현</li>
</ul>
</li>
<li>참여자 전원은 이전 블록들을 저장하고 있으므로 새로운 블록의 무결성을 확인가능</li>
<li>새롭게 제안되는 블록은 참여자들이 검증 및 합의할 수 있는 형태여야 함.(투명성)</li>
<li>한번 쓰여진 블록은 이전의 합의를 번복할 수 있지 않는 한 변경될 수 없음. (불변성)</li>
</ul>
<p><br><br></p>
<h2 id="합의-알고리즘">합의 알고리즘</h2>
<table>
<thead>
<tr>
<th></th>
<th>PoW</th>
<th>PoS</th>
<th>BFT-variants</th>
</tr>
</thead>
<tbody><tr>
<td>제안자격 취득 방법</td>
<td>계산이 어려운 문제를 풀 것</td>
<td>플랫폼 토큰을 보유한 기간에 따라 결정적으로 또는 확률적으로 뽑힐 것</td>
<td>정해진 순서 또는 정해진 확률에 의해 뽑힐 것</td>
</tr>
<tr>
<td>네트워크 참여 제한</td>
<td>없음</td>
<td>없거나 낮음</td>
<td>높음</td>
</tr>
<tr>
<td>합의에 필요한 연산량</td>
<td>높음</td>
<td>낮음</td>
<td>낮음</td>
</tr>
<tr>
<td>위협</td>
<td>전체 연산량의 51%를 한 참여자가 소유할 경우 중앙화 됨</td>
<td>전체 토큰의 51%를 한 참여자가 소유할 경우 중앙화 됨</td>
<td>전체 참여노드의 1/3 이상이 담합할 경우 합의 불가. 전체 참여노드의 2/3 이상이 담합할 경우 중앙화됨</td>
</tr>
<tr>
<td>대표적인 블록체인</td>
<td>Bitcoin, Litecoin, Ethereum, Monero, QTUM</td>
<td>Ehtereum FFG &amp; CFG, EOS(dPos)</td>
<td>Klayton, Tendermint, Hyperledger Fabric</td>
</tr>
<tr>
<td>- PoW는 네트워크 참여 제한이 없지만, difficulty가 증가하면서 진입장벽이 생기게 된다.</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>- PoS는 토큰을 많이 보유한 사람이 유리한 구조이기 때문에, 바로 PoS를 도입하지는 않는다. PoW를 통해 토큰을 다양한 곳에 분포시킨 이후, PoS를 적용한다.</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>- BFT에서는 각 노드들이 네트워크에 참여하고 있는 모든 노드들을 알고있다. 네트워크에 참여하고 있는 노드들이 많아질 수록 합의를 위한 통신이 많아진다. 따라서, 합의가 느려지게 된다. BFT에서는 2/3가 합의에 대해 승인해야 블록이 인정된다.</td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody></table>
<p><br><br></p>
<h2 id="블록체인의-비교">블록체인의 비교</h2>
<h3 id="public-vs-private">Public vs. Private</h3>
<p>퍼블릭과 프라이빗의 구분은 블록체인에 대해 다음 사항을 수행할 수 있는지를 확인하여 결정한다.</p>
<ul>
<li>누군든지 기록된 정보(블록)을 자유롭게 읽을 수 있는가?</li>
<li>명시적인 등록 또는 자격취득 없이 정보를 블록체인 네트워크에 기록할 수 있는지?</li>
</ul>
<p>블록체인의 정보가 공개되어 있고 네트워크가 정한 기준(e.g., gas fee)에 따라 정보를 기록요청할 수 있다면 그 블록체인은 <strong>퍼블릭/공개형</strong>이라 한다.</p>
<p>이와 반대로 정보가 공개되어 있지 않고 미리 자격을 득한 사용자만이 정보를 기록할 수 있다면 그 블록체인은 <strong>프라이빗/비공개</strong>형이라 한다.</p>
<h3 id="permissionless-vs-permissioned">Permissionless vs. Permissioned</h3>
<p>일반적으로 네트워크의 참여가 제한된 경우 ‘permissioned’, 그렇지 않은 경우 ‘permissionless’라 정의한다.</p>
<p>네트워크의 참여의 정의</p>
<ul>
<li>(넓은 의미) 블록체인 P2P 네트워크에 참여</li>
<li>(좁은 의미) 합의과정의 참여</li>
</ul>
<p>Public/Private의 개념이 정보의 <strong>접근성(Acecss)</strong>와 관련이 있다면 Permissionless/Permissioned는 정보의 <strong>제어(Control)</strong>, 즉 무엇이 블록에 포함되는지를 결정하는 지에 더 밀접한 개념이다.</p>
<p><br><br></p>
<h2 id="공개키-암호화와-전자서명">공개키 암호화와 전자서명</h2>
<h3 id="암호">암호</h3>
<ul>
<li><p>고전적인 암호 : 카이사르 암호</p>
<p>  암호하려는 내용을 아파벳별로 일정한 거리(distance, d)만큼 밀어서 다른 알파벳으로 치환하는 기법. 암호를 풀거나 만들때 알아야하는 정보를 키(Key)라고 함. 위 예제에서 사용된 키는 d=3.</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/jmsuper_97/post/62d28429-7766-4a67-aebf-c549342de276/image.png" alt=""></p>
<h3 id="대칭키암호비대칭키암호">대칭키암호/비대칭키암호</h3>
<p>평문(Plain text)이란 암호화 되어 있지 않은 문자열을 의미</p>
<ul>
<li>암호화는 평문을 암호로 만드는 것 (cipher, encrypt)</li>
<li>복호화는 암호를 평문으로 만드는 것 (decipher, decrypt)</li>
</ul>
<p>암호화에 사용한 키와 복호화에 사용한 키가 동일한 경우 대칭키암호로 분류</p>
<p>암호화에 사용한 키와 복호화에 사용한 키가 다를 경우 비대칭키암호로 분류</p>
<h3 id="비대칭키암호공개키암호">비대칭키암호(공개키암호)</h3>
<p>블록체인에서는 비대칭키암호를 사용한다.</p>
<p>두개의 키를 사용하여 암호화와 복호화를 실행</p>
<ul>
<li>암호화에 사용되는 키 = 공개키(Public Key, PK)</li>
<li>복호화에 사용되는 키 = 비밀키(Private Key/Secret Key, SK)</li>
</ul>
<p>비대칭키 암호의 목적:</p>
<p>“누구든지 암호화할 수 있지만 <strong>비밀키를 아는 사람만 복호화</strong>할 수 있어야 한다”</p>
<ul>
<li>공개키와 비밀키는 한쌍으로 묶여있는 아주 큰 숫자들</li>
<li>비밀키로부터 공개키를 도출하는 것은 쉬움</li>
<li>공개키로부터 비밀키를 찾는 것은 매우 어려움</li>
</ul>
<h3 id="공개키암호를-사용한-안전한-통신">공개키암호를 사용한 안전한 통신</h3>
<p><img src="https://velog.velcdn.com/images/jmsuper_97/post/93240392-bd83-429f-b2f1-403e5c58e131/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/jmsuper_97/post/ba723cce-1184-4bea-90e5-2ee3a63d530f/image.png" alt=""></p>
<h3 id="전자서명">전자서명</h3>
<p>비대칭키암호는 지정된 사람만 정보를 확인할 수 있도록 도움(Privacy)</p>
<ul>
<li>Alice가 Bob에게 메세지를 보낼 때 PK(Bob)을 사용</li>
<li>Bob은 이 메세지가 Alice에게서 온 것인지 어떻게 확인할까?</li>
</ul>
<p>전자서명은 누가 정보를 보냈는지 알기 위해 사용(non-repudiation)</p>
<ul>
<li>전자서명은 비대칭암호의 응용 프로그램</li>
<li>서명은 비밀키로만 생성가능</li>
<li>공개키는 서명이 짝을 이루는 비밀키로 생성되었는지를 검증</li>
</ul>
<h3 id="공개키암호와-전자서명을-사용한-안전한-통신">공개키암호와 전자서명을 사용한 안전한 통신</h3>
<p><img src="https://velog.velcdn.com/images/jmsuper_97/post/80289de1-38de-419d-bacb-c73c03293e7f/image.png" alt=""></p>
<ul>
<li>해싱을 추가한 경우(메세지가 변조됬지 않았다는 것을 증명)</li>
</ul>
<p><img src="https://velog.velcdn.com/images/jmsuper_97/post/b7aa8e1a-ccf4-4790-a9ea-8b86ddf767d7/image.png" alt=""></p>
<p><br><br></p>
<h2 id="블록체인에서-사용되는-암호화-기법">블록체인에서 사용되는 암호화 기법</h2>
<p><img src="https://velog.velcdn.com/images/jmsuper_97/post/b5448e30-2012-4c7c-a837-2cb725fd8f7c/image.png" alt=""></p>
<ul>
<li>블록체인은 암호학적 기법을 토대로 만들어진 기술</li>
<li>Bitcoin은 네트워크 참여자 모두가 같은 ‘원장’을 공유함으로써 투명한 거래가 가능</li>
<li>원장은 어느 주소에 BTC가 있는지 기록하지만 그 주소가 누구에게 속하는지는 기록하지않음(anonymity)</li>
<li>Bitcoin은 공개키암호를 사용하여 명시적인 비밀교환과정 없이 BTC의 소유권 증명을 실행</li>
</ul>
<h3 id="공개키암호화를-사용한-소유권-증명">공개키암호화를 사용한 소유권 증명</h3>
<p><img src="https://velog.velcdn.com/images/jmsuper_97/post/12d19739-e231-496d-ae7b-46096f007974/image.png" alt=""></p>
<ul>
<li><p>대부분의 블록체인 주소는 공개키로부터 도출된 값</p>
<ul>
<li>Bitcoin : Hash 160 of a public key where Hash 16 = RIPEMD 160 + SHA256</li>
<li>Ethereum : Rightmost 160 bits of Keccak hash of public key</li>
</ul>
</li>
<li><p>Bitcoin의 경우</p>
<p>  임의의 주소 X에 10 BTC가 있다고 가정할 때 Alice는 X에서 또다른 임의의 주소 Y로 5 BTC를 전송(i.e., transfer 5 BTC X to Y)하는 거래를 성사시키기 위해 X로 변환되는 공개키와 짝을 이루는 비밀키로 해당 거래를 서명할 수 있어야 한다.</p>
<ul>
<li><p>실행과정</p>
<p>  X에서 transaction을 비밀키로 암호화 → 암호화된 transaction을 공개키와 함께 네트워크에 전송(공개키 256bit + 암호화된 transaction 256bit = 516bit) → 노드들은 공개키로 암호화된 transaction을 복호화 &amp; 복호화된 transaction의 to_address가 공개키에서 도출가능한 것인지 확인 → 만약 성립한다면 transaction을 인정</p>
</li>
</ul>
</li>
<li><p>Ethereum의 경우</p>
<p>  임의의 주소 X에 위치한 어카운트의 잔고에 10 ETH가 있다고 가정할 때 Alice는 X에서 또다른 임의의 주소 Y에 위치한 어카운트로 5 ETH를 전송(i.e., transfer 5 ETH from the account at X to the account at Y)하는 거래를 성사시키기 위해 X로 변환되는 공개키와 짝을 이루는 비밀키로 해당 거래를 서명할 수 있어야 한다.</p>
<ul>
<li><p>실행과정</p>
<p>  Bitcoin과 유사하지만, 특정 기법을 활용하여 512bit를 256bit로 축소</p>
</li>
</ul>
</li>
</ul>
<h3 id="구현-방법으로-나눠본-블록체인">구현 방법으로 나눠본 블록체인</h3>
<p>UTXO는 코인에 주소를 부여하는 방법이고, 어카운트 기반은 어카운트(계좌)에 주소를 부여하고 어카운트에 balance라는 상태값이 존재하는 방법이다.</p>
<p><strong>UTXO(Unspent Transaction Output) 기반 블록체인</strong></p>
<ul>
<li>블록체인에 사용 가능한 토큰(e.g., Bitcoin) - UTXO들과 사용 자격검증방법을 기록</li>
<li>일반적인 자격검증방법은 UTXO의 정보와 일치하는 공개키로 검증가능한 전자서명을 제출하는 것</li>
<li>Bitcoin이 대표적인 UTXO 기반 블록체인</li>
<li>돈과 전자서명이 매칭할 수 있어 병렬 작업에 용이하다.</li>
</ul>
<p><strong>어카운트 기반 블록체인(Account-based Blockchain)</strong></p>
<ul>
<li>어카운트는 블록체인을 구성하는 주체(entity)를 표현하며 상태를 기록</li>
<li>사용자는 어카운트를 사용할 때마다 어카운트 공개키로 검증가능한 전자서명을 생성</li>
<li>상태를 기록할 수 있기 때문에 스마트 컨트랙트를 구현하기에 용이</li>
<li>Ethereum, Klayton이 대표적인 어카운트 기반 블록체인</li>
<li>어카운트에서 발생하는 트랜잭션들의 순서가 바뀌는 결과값이 바뀌기 때문에, 한 어카운트에서 동시에 여러 트랜잭션을 수행할 수 없다.</li>
<li>대신 상태를 가지고 있기 때문에 스마트 컨트랙트 구현이 가능하다.</li>
</ul>
<h3 id="ethereum-어카운트-주소-상태">Ethereum 어카운트, 주소, 상태</h3>
<ul>
<li>Ethereum의 어카운트는 Ethereum의 주체(entity)를 표현하고 그 상태를 기록하는데 사용</li>
<li>어카운트는 <strong>EOA(Externally Owned Account)</strong>와 <strong>스마트 컨트랙트</strong>로 구분</li>
<li>Ethereum 사용자는 EOA를 사용</li>
<li>사용자는 임의의 공개키와 비밀키 쌍(Key Pair)을 생성한뒤 공개키를 어카운트 주소로 변환하여 EOA를 생성<ul>
<li>별도의 승인과정이 필요없으며 Ethereum 네트워크와 통신도 필요없음</li>
<li>위 과정으로 인해 어카운트는 특정 키페어에 종속</li>
</ul>
</li>
<li>사용자의 상태(state)는 어카운트 주소로 찾을 수 있는 블록체인 저장공간에 기록</li>
<li>비밀키 → 공개키 → 주소</li>
</ul>
<h3 id="트랜잭션transaction-tx">트랜잭션(Transaction, TX)</h3>
<p><img src="https://velog.velcdn.com/images/jmsuper_97/post/7d15430a-1718-4421-82a7-00260547505a/image.png" alt=""></p>
<ul>
<li>블록은 트랜잭션들을 일정한 순서로 정렬하여 저장하는 컨테이너</li>
<li>트랜잭션은 어카운트의 행동</li>
<li>트랜잭션의 순서는 중요: <code>TX_1 → TX_2</code> 와 <code>TX_2 → TX_1</code> 은 다름</li>
<li>블록체인 참여자들은 블록을 검증할 때 트랜잭션들이 올바른 순서대로 정렬되었는지를 확인 후 합의</li>
<li>각각의 트랜잭션들은 어카운트에 연결된 공개키로 검증간으한 서명을 포함</li>
</ul>
<h3 id="transaction-journey트랙잭션이-블록에-저장되는-과정">Transaction Journey(트랙잭션이 블록에 저장되는 과정)</h3>
<p><img src="https://velog.velcdn.com/images/jmsuper_97/post/8c290a34-54f8-4969-959c-68230773d2fb/image.png" alt=""></p>
<ol>
<li>Alice는 노드에게 TX를 전달한다.</li>
<li>노드는 다른 노드들에게 TX를 전달한다.</li>
<li>그러던 중, 특정 노드에서 블록을 생성하게 된다.</li>
<li>해당 블록에는 Alice의 TX가 포함되있고, 해당 TX는 1 confirmaion을 받았다고 말한다.</li>
<li>해당 블록 위로 다른 블록들이 쌓일수록 해당 TX의 confirmaion은 증가한다.</li>
</ol>
<h3 id="confirmation-vs-finality">Confirmation vs. Finality</h3>
<ul>
<li><p>Confirmation 숫자는 트랜잭션이 블록에 포함된 이후 생성된 블록의 숫자</p>
<ul>
<li>임의의 트랜잭션 T가 포함된 블록의 높이가 100, 현재 블록높이가 105라면 T의 confirmation 숫자는 6</li>
</ul>
</li>
<li><p>PoW를 사용하는 블록체인들은 finality가 없기 때문에 confirmation 숫자가 중요</p>
</li>
<li><p>Finality란 블록의 완결성을 의미</p>
<ul>
<li>합의를 통해 생성된 블록이 번복되지 않을 경우 완결성이 존재</li>
</ul>
</li>
<li><p>PoW 기반 합의는 확률에 기반하기 때문에 경우에 따라 블록이 사라질 수 있으므로 완결성이 부재함</p>
<ul>
<li>PoW 블록체인은 수학적으로 복잡한 퍼즐을 풀어 블록을 제안할 자격을 얻는 구조</li>
<li>만약 두명의 서로 다른 참여자가 동시에 퍼즐을 풀어 두개의 올바른 블록을 생성한다면 두 블록 중 하나는 (eventurally) 사라지게 됨</li>
<li>이 때문에 블록이 확률적 완결성을 갖기까지 일정 갯수 이상의 블록이 생성되기를 기다려야 함.</li>
</ul>
</li>
<li><p>예시</p>
<p>  비트코인이나 이더리움 같은 블록체인은 규모가 크기 때문에 전 세계에 노드들이 분포되있다. 만약 양 끝 지점에 있는 노드에서 각각 블록을 동시에 생성할 경우 어떤 일이 발생할까? 두 노드 주변에 있는 노드들을 본인들의 블록이 옳다고 인정할 것이다. 하지만, 가운데 있는 노드들은 어떤 블록이 옳은 블록인지 알지 못한다. 따라서, 더 긴 블록체인에 포함되 있는 블록이 (eventually) 선택되고, 나머지 블록은 사라질 것이다.</p>
</li>
</ul>
<h3 id="understanding-bitcoins-6-confirmations-rule">Understanding Bitcoin’s 6 Confirmations Rule</h3>
<ul>
<li>네트워크 시차로 인해 생성된 우연한 복수의 블록들 가운데 하나가 선택되는데 필요한 블록은 두어개 정도 → 2~3 confirmations</li>
<li>퍼즐을 빠르게 풀 수 있는 악의적인 참여자(공격자)가 있을 경우 그 참여자의 해시능력(hash power)에 따라 필요한 confirmation 숫자가 달라짐<ul>
<li>해시능력이 높을 수록 퍼즐을 푸는 속도도 빠르기 때문에 주어진 문제를 먼저 풀 확률이 높아짐</li>
<li>해시능력이 높은 참여자는 longest chain을 임의로 선택 또는 생성할 수 있음</li>
<li>따라서 해시능력을 감안하더라도 임의로 블록체인을 변경하지 못할 정도로 충분히 많은 블록이 생성되기를 기다려야할 필요가 생김</li>
<li>Bitcoin의 6 confirmation 법칙은 공격자가 전체 해시능력의 약 25%를 가질 때를 가정한 숫자</li>
</ul>
</li>
</ul>
<h3 id="bft-기반-블록체인">BFT 기반 블록체인</h3>
<ul>
<li>BFT 기반 블록체인은 블록의 완결성이 보장됨<ul>
<li>네트워크가 동기화되어 있기 때문</li>
<li>블록 생성이 PoW에 비해 빠르고 경제적</li>
</ul>
</li>
<li>하지만 네트워크 동기화의 필요로 인해 참여자의 숫자가 제한됨<ul>
<li>네트워크 참여자 구성이 고정되어 있어야 합의가 가능</li>
<li>구성이 변경될 경우 모든 네트워크 참여자가 새로운 구성을 인지하기 까지 합의 불가능</li>
<li>합의 알고리즘이 네트워크 동기화를 가정하고 짜여졌기 때문에 네트워크 사용량이 높음</li>
<li>참여자가 많아질 경우 네트워크 오버헤드로 인해 합의가 느림</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] 자바로 구현하는 HTTP 서버]]></title>
            <link>https://velog.io/@jmsuper_97/Java-%EC%9E%90%EB%B0%94%EB%A1%9C-%EA%B5%AC%ED%98%84%ED%95%98%EB%8A%94-HTTP-%EC%84%9C%EB%B2%84</link>
            <guid>https://velog.io/@jmsuper_97/Java-%EC%9E%90%EB%B0%94%EB%A1%9C-%EA%B5%AC%ED%98%84%ED%95%98%EB%8A%94-HTTP-%EC%84%9C%EB%B2%84</guid>
            <pubDate>Wed, 19 Oct 2022 14:46:30 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>웹 서버와 관련 포스팅 중, 웹 서버 프로그램을 사용하는 당위성을 설명하기 위해 직접 Java를 사용하여 HTTP 서버를 구현하고자 합니다. 간단하게 GET 요청만 받는 HTTP 서버를 구현합니다.</p>
</blockquote>
<h2 id="http서버를-구현하기-위한-요구사항">HTTP서버를 구현하기 위한 요구사항</h2>
<ul>
<li>서버 호스트 주소</li>
<li>HTTP서버 포트 번호</li>
<li>클라이언트의 HTTP Request 파싱</li>
<li>HTTP Request 클래스</li>
<li>HTML 파일을 HTTP Reponse body에 집어넣기</li>
<li>HTTP Response 클래스</li>
<li>HTTP 요청을 캐치하는 서버 프로그램</li>
</ul>
<br>

<blockquote>
<p><strong>Server Socket API</strong>
ServerSocket의 클래스는 서버 프로그램을 구현하는데 사용됩니다. 일반적인 서버 프로그램의 과정은 Step1 ~ Step 6으로 나눌 수 있습니다.
Step 1. 서버 소켓 생성, 포트 바인딩
Step 2. 클라이언트로부터의 연결을 기다리고(Connect Listen) 요청이 오면 수락
Step 3. 클라이언트 소켓에서 가져온 InputStream(클라이언트 쪽에서는 OuputStream 이 됩니다)을 읽음
Step 4. 응답이 있다면 OutputStream을 통해 클라이언트에 데이터를 보냄
Step 5. 클라이언트와의 연결을 닫음
Step 6. 서버 종료
출처: <a href="https://woolbro.tistory.com/29">https://woolbro.tistory.com/29</a> [개발자 울이 노트:티스토리]</p>
</blockquote>
<br>

<h2 id="구현-계획">구현 계획</h2>
<ol start="0">
<li>구현 계획을 기반으로 클래스 다이어그램 작성</li>
<li>클라이언트가 서버 프로그램에 요청을 보냈을 때, 서버 프로그램이 요청을 <code>catch</code>할 수 있는 지 테스트</li>
<li>서버가 <code>HTTP Request</code>을 받아들일 때 어떠한 구조로 받아들이는지 <code>출력문</code>을 통해 확인</li>
<li>입력 받은 <code>HTTP Request</code>의 <code>Type</code> 을 확인하고, 해당 타입을 <code>DTO</code>로 전환할 수 있도록 클래스 작성</li>
<li>서버 프로그램에서 <code>DTO</code>를 활용하여 <code>HTTP Request</code> 를 객체로 전환</li>
<li><code>HTTP Request</code> 객체의 헤더를 확인하여 적절한 <code>handler method</code> 호출</li>
<li><code>handler method</code> 가 반환한 <code>HTTP Response</code> 객체를 클라이언트에게 응답</li>
</ol>
<br>

<h2 id="구현">구현</h2>
<h4 id="websocketjava">WebSocket.java</h4>
<pre><code class="language-java">// 이 예제는 소켓을 활용하여 HTTP서버를 만들어 보기 위한 예제입니다.
// 테스트를 위해 HTTP 동작만 확인했기 때문에, 코드를 참조할 경우
// HTTP request or response 코드만 확인하시고,
// 소켓 관련 코드는 추가적인 정보를 찾아 올바른 코드를 활용하시기 바랍니다.
public class WebSocket {
    public static void main(String[] args) {
        System.out.println(&quot;Server has started on 127.0.0.1:8080 \nWaiting for a connection...&quot;);
        try {
            while(true){
                ServerSocket socket = new ServerSocket(8080);
                Socket client = socket.accept();
                System.out.println(&quot;Client connected!&quot;);

                InputStream in = client.getInputStream();
                OutputStream out = client.getOutputStream();
                Scanner s = new Scanner(in,&quot;UTF-8&quot;);
                String requestData = null;
                if(s.hasNextLine()){
                    requestData = s.nextLine();
                }

                HttpRequestDto httpRequestDto = new HttpRequestDto(requestData);
                Dispatcher dispatcher = new Dispatcher();
                HttpHandler httpHandler = dispatcher.dispatch(httpRequestDto);
                HttpResponseDto httpResponseDto = httpHandler.handle();
                out.write(httpResponseDto.getResponseData().getBytes());

                s.close();
                in.close();
                out.close();
                socket.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}</code></pre>
<br>

<h4 id="httprequestdtojava">HttpRequestDto.java</h4>
<pre><code class="language-java">public class HttpRequestDto {
    private String method;
    private String requestUrl;
    public HttpRequestDto(String httpRequest){
        ArrayList&lt;String&gt; requestList = new ArrayList&lt;&gt;(Arrays.asList(httpRequest.split(&quot; &quot;)));
        this.method = requestList.get(0);
        this.requestUrl = requestList.get(1);
    }

    public String getMethod() {
        return method;
    }

    public String getRequestUrl() {
        return requestUrl;
    }
}</code></pre>
<br>

<h4 id="httpresponsedtojava">HttpResponseDto.java</h4>
<pre><code class="language-java">public class HttpResponseDto {
    private String responseData;

    public HttpResponseDto(String responseData){
        this.responseData = responseData;
    }

    public String getResponseData() {
        return responseData;
    }
}</code></pre>
<br>

<h4 id="httphandlerjavainterface">HttpHandler.java(interface)</h4>
<pre><code class="language-java">public interface HttpHandler {
    HttpResponseDto handle();
}
</code></pre>
<br>

<h4 id="defaulthttphandlerjavaimplementation">DefaultHttpHandler.java(implementation)</h4>
<pre><code class="language-java">public class DefaultHttpHandler implements HttpHandler{

    @Override
    public HttpResponseDto handle() {
        String responseData = &quot;HTTP/1.1 200 OK\r\n&quot; +
                &quot;Server: \r\n&quot; +
                &quot;Content-type: text/html\r\n\r\n&quot; +
                &quot;&lt;HTML&gt;&quot; +
                &quot;&lt;HEAD&gt;&lt;TITLE&gt;200&lt;/TITLE&gt;&lt;/HEAD&gt;&quot; +
                &quot;&lt;BODY&gt;This is \&quot;/\&quot; Response Data!&quot; +
                &quot;&lt;/BODY&gt;&lt;/HTML&gt;&quot;;
        return new HttpResponseDto(responseData);
    }
}</code></pre>
<br>

<h4 id="hellohttphandlerjavaimplementation">HelloHttpHandler.java(implementation)</h4>
<pre><code class="language-java">public class HelloHttpHandler implements HttpHandler {

    @Override
    public HttpResponseDto handle() {
        String responseData = &quot;HTTP/1.1 200 OK\r\n&quot; +
                &quot;Server: \r\n&quot; +
                &quot;Content-type: text/html\r\n\r\n&quot; +
                &quot;&lt;HTML&gt;&quot; +
                &quot;&lt;HEAD&gt;&lt;TITLE&gt;200&lt;/TITLE&gt;&lt;/HEAD&gt;&quot; +
                &quot;&lt;BODY&gt;This is \&quot;/hello\&quot; Response Data!&quot; +
                &quot;&lt;br&gt;hello world!&quot;+
                &quot;&lt;/BODY&gt;&lt;/HTML&gt;&quot;;
        return new HttpResponseDto(responseData);
    }
}</code></pre>
<br>

<h4 id="dispatcherjava">Dispatcher.java</h4>
<pre><code class="language-java">public class Dispatcher {
    public HttpHandler dispatch(HttpRequestDto requestDto) throws Exception {
        String method = requestDto.getMethod();
        String requestUrl = requestDto.getRequestUrl();
        if(method.equals(&quot;GET&quot;)){
            if(requestUrl.equals(&quot;/&quot;)){
                return new DefaultHttpHandler();
            }else if(requestUrl.equals(&quot;/hello&quot;)){
                return new HelloHttpHandler();
            }else if(requestUrl.equals(&quot;/favicon.ico&quot;)){
                return new HelloHttpHandler();
            }
        }else{
            throw new Exception(&quot;Not supported Method!&quot;);
        }
        return null;
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[스프링] 1. SQL & JDBC 프로그래밍 - JDBC]]></title>
            <link>https://velog.io/@jmsuper_97/Spring-%EB%B6%80%EC%8A%A4%ED%8A%B8%EC%BD%94%EC%8A%A4-%EC%9B%B9%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B3%BC%EC%A0%95-1.-SQL-JDBC-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-JDBC</link>
            <guid>https://velog.io/@jmsuper_97/Spring-%EB%B6%80%EC%8A%A4%ED%8A%B8%EC%BD%94%EC%8A%A4-%EC%9B%B9%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B3%BC%EC%A0%95-1.-SQL-JDBC-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-JDBC</guid>
            <pubDate>Wed, 19 Oct 2022 11:39:41 GMT</pubDate>
            <description><![CDATA[<h2 id="jdbc">JDBC</h2>
<blockquote>
<p>참고링크 : <a href="https://debugdaldal.tistory.com/181">https://debugdaldal.tistory.com/181</a></p>
</blockquote>
<h3 id="jdbc의-정의">JDBC의 정의</h3>
<ul>
<li>자바를 이용한 데이터베이스 접속과 SQL 문장의 실행, 그리고 실행 결과로 얻어진 데이터의 핸들링을 제공하는 방법과 절차에 관한 규약</li>
<li>자바 프로그램내에서 SQL문을 실행하기 위한 자바 API</li>
<li>SQL과 프로그래밍 언어의 통합 접근 중 한 형태</li>
<li>매번 SQL문을 입력하여 실행할 수 없기 떄문에 이를 프로그래밍화 한 것이다.</li>
<li>JAVA는 표준 인터페이스인 JDBC API를 제공한다. 또한 데이터베이스 벤더, 또는 써드파티에서는 JDBC 인터페이스를 구현한 드라이버를 제공한다. </li>
<li><strong>벤더란?</strong> 판매인 또는 판매업자를 가리키는 말. 예를 들어 컴퓨터의 하드웨어나 소프트웨어 제품이 판매되는 경우, 해당 제품에 대해 책임을 지는 기업을 말한다.</li>
</ul>
<br>

<h3 id="필요한-환경-구성">필요한 환경 구성</h3>
<ul>
<li>JDK 설치</li>
<li>JDBC 드라이버 설치 : pom.xml의 dependencies에 추가<pre><code>&lt;dependency&gt;   
&lt;groupId&gt;mysql&lt;/groupId&gt;   
     &lt;artifactId&gt;mysql-connector-java&lt;/artifactId&gt;
     &lt;version&gt;5.1.45&lt;/version&gt;
&lt;/dependency&gt;</code></pre></li>
</ul>
<br>

<h3 id="jdbc-사용---단계별-정리">JDBC 사용 - 단계별 정리</h3>
<ol>
<li><code>import java.sql.*;</code></li>
<li>드라이버를 로드한다.(각각의 DB벤더가 정한 절차를 따른다.)</li>
<li>Connection 객체를 생성한다.(DB접속)</li>
<li>Statement 객체 생성 및 질의 수행</li>
<li>SQL문에 결과물이 있다면 ResultSet 객체를 생성한다.</li>
<li>모든 객체를 닫는다.
(항상 연결하는게 있으면 닫아야 한다. DB는 접속할 수 있는 클라이언트가 무한대가 아니기 때문이다.)</li>
</ol>
<br>

<h3 id="jdbc-클래스의-생성관계">JDBC 클래스의 생성관계</h3>
<p><img src="https://velog.velcdn.com/images/jmsuper_97/post/6a325ce5-3c42-40a0-aaac-e53ebb3c543e/image.png" alt=""></p>
<p><code>DriverManager를 통해 Connection객체 생성</code> -&gt; <code>Connection객체를 통해 Statement객체 생성</code> -&gt; <code>Statement객체를 통해 ResultSet객체 생성</code>
닫을 때는 역순이다.
<code>ResultSet객체 해제</code> -&gt; <code>Statement객체 해제</code> -&gt; <code>Connection객체 해제</code><br></p>
<br>

<h3 id="단계별-설명1">단계별 설명1</h3>
<ul>
<li>IMPORT
<code>import java.sql.*</code></li>
<li>드라이버 로드
<code>Class.forName(&quot;com.mysql.jdbc.Driver&quot;)</code>;<br>
DB벤더에서 제공하는 객체이다. Class클래스의 forName메소드를 이용하면 해당 객체가<br>
메모리에 올라간다. new를 통한 객체 생성과 비슷한 동작을 한다. 어떤 벤더사의 DB를 사용하냐에<br>
따라서 forName()의 인자값은 달라진다.<br></li>
<li>Connection 얻기<ul>
<li><code>String dburl = &quot;jdbc:mysql://localhost/dbName&quot;;</code></li>
<li><code>Connection con = DriverManager.getConnection(dburl,ID,PWD);</code></li>
</ul>
</li>
</ul>
<br>  

<h3 id="단계별-설명2">단계별 설명2</h3>
<ul>
<li>Statement 생성
<code>Statement stmt = con.createStatement();</code></li>
<li>질의 수행
<code>ResultSet rs = stmt.executeQuery(&quot;select no from user&quot;);</code><br>
어떤 쿼리를 이용하는지에 따라 실행명령어는 달라진다.<ul>
<li>any SQL : execute()</li>
<li>SELECT : executeQuery()</li>
<li>INSERT, UPDATE, DELETE : executeUpdate()</li>
</ul>
</li>
</ul>
<br>

<h3 id="단계별-설명3">단계별 설명3</h3>
<ul>
<li><p>ResultSet으로 결과 받기</p>
<pre><code>ResultSet rs = stmt.executeQuery(&quot;select no from user&quot;);
while(rs.next())
  System.out.println(rs.getInt(&quot;no&quot;));</code></pre><p>ResultSet객체인 rs에 저장되는 것은 쿼리 수행 결과값이 아니다. 결과값은 DB가 가지고 있고, ResultSet객체에 저장되는 것은 해당 결과를 가리키는 레퍼런스이다. 왜냐하면 쿼리결과가 10,000건이 넘는 경우, 이를 서버에 바로 전달하면 서버에 무리가 가기 때문에, 서버에서 필요한 값을 꺼내오는 형식으로 진행된다. 이떄 사용하는 메소드가 next()이다. next()는 처음에 쿼리 결과의 첫번째 레코드를 가리키며, 실행 이후에는 다음 레코드를 가리키게 된다.<br></p>
</li>
<li><p>Close</p>
<ul>
<li><code>rs.close();</code></li>
<li><code>stmt.close();</code></li>
<li><code>con.close();</code></li>
</ul>
</li>
<li><p>전체 소스코드</p>
<pre><code class="language-java">import java.sql.*;

</code></pre>
</li>
</ul>
<p>public class Main {</p>
<pre><code>public static void main(String[] args) throws Exception {
    Connection conn = null;
    Statement stmt = null;
    ResultSet rs = null;
    try {
        conn = getConnection();
        stmt = conn.createStatement();
        rs = stmt.executeQuery(&quot;select id from guestbook;&quot;);
        while(rs.next()) System.out.println(rs.getInt(&quot;id&quot;));
    } catch (Exception e) {
        e.printStackTrace();
    }finally {
        if(rs != null) {
            rs.close();
        }
        if(stmt != null) {
            stmt.close();
        }
        if(conn != null) {
            conn.close();
        }
    }
}

public static Connection getConnection() throws Exception{
    Class.forName(&quot;com.mysql.jdbc.Driver&quot;);

    String dburl = &quot;jdbc:mysql://localhost:3306/connectdb?useSSL=false&quot;;
    String ID = &quot;root&quot;;
    String PWD = &quot;1111&quot;;

    Connection conn = DriverManager.getConnection(dburl,ID,PWD);
    return conn;
}    </code></pre><p>}</p>
<pre><code>
&lt;br&gt;&lt;br&gt;

### 의문점
```java
public class Main {

    public static void main(String[] args) {
        try {
            Class.forName(&quot;com.mysql.jdbc.Driver&quot;);

            String dburl = &quot;jdbc:mysql://localhost:3306/connectdb?useSSL=false&quot;;
            String ID = &quot;root&quot;;
            String PWD = &quot;1111&quot;;

            Connection conn = DriverManager.getConnection(dburl,ID,PWD);
            System.out.println(conn.getClass());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}</code></pre><p>위 코드에서 <code>Class.forName</code> 의 역할은 무엇일까? 객체를 넘겨받지도 않기 때문에, 왜 필요한 코드인지 의문이 갔다.</p>
<p><strong>Class.forName</strong></p>
<p>위 메소드는 <code>static</code> 메소드이다. 메소드를 호출하면, 먼저 클래스로더가 해당 클래스의 클래스파일을 찾는다. 발견되면 클래스파일을 보고 <code>Class&lt;?&gt;</code> 의 인스턴스(특정 클래스의 메타 데이터)를 생성하여 JVM의 Method Area(Static Area)에 업로드 하게 된다. 만약 Method Area 영역에 메타 데이터가 존재한다면 추가로  <code>Class&lt;?&gt;</code> 의 인스턴스 생성하지 않을 것이다.</p>
<p>jdbc Driver를 사용하려면 해당 드라이버가 JVM에 로드되어 있어야 한다. 자바는 lazy loading을 지원하므로 jdbc Driver가 JVM에 로드되있지 않을 수 있다. 이를 대비하여 명시적으로 드라이버를 로드하는 것이다.</p>
<pre><code class="language-java">public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    //
    // Register ourselves with the DriverManager
    //
    static {
        try {
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException E) {
            throw new RuntimeException(&quot;Can&#39;t register driver!&quot;);
        }
    }

    /**
     * Construct a new driver and register it with DriverManager
     * 
     * @throws SQLException
     *             if a database error occurs.
     */
    public Driver() throws SQLException {
        // Required for Class.forName().newInstance()
    }
}</code></pre>
<p>위 코드는 com.mysql.Driver 클래스의 코드이다. 위 클래스가 로드되면 static블럭이 실행되고 java.sql.DriverManager에 해당 클래스의 인스턴스가 등록된다.</p>
<pre><code class="language-java">Class.forName(&quot;com.mysql.jdbc.Driver&quot;);
String dburl = &quot;jdbc:mysql://localhost:3306/connectdb?useSSL=false&quot;;
String ID = &quot;root&quot;;
String PWD = &quot;1111&quot;;
Connection conn = DriverManager.getConnection(dburl,ID,PWD);</code></pre>
<p>이 때문에, DriverManager에서 connection을 가져올 수 있는 것이다.</p>
<br>


<h3 id="생각해보기">생각해보기</h3>
<ul>
<li>java.sql 외에 JAVA가 인터페이스만 대부분 제공하는 패키지는 또 어떤 것이 있을까요?
JAVA가 인터페이스만 제공하는 패키지는 자바 ORM 명세인 JPA가 있다.
자바ORM이란? 객체와 DB의 테이블 매핑을 이루는 것. SQL문 작성없이 DB데이터를 JAVA객체로 받을 수 있다.
JPA란? 자바 ORM기술에 대한 API표준 명세.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[네트워크] 2. 웹 백엔드 프로그래밍 기초 - HTTP와 HTTPS의 차이점]]></title>
            <link>https://velog.io/@jmsuper_97/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-HTTP%EC%99%80-HTTPS%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90</link>
            <guid>https://velog.io/@jmsuper_97/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-HTTP%EC%99%80-HTTPS%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90</guid>
            <pubDate>Wed, 12 Oct 2022 11:53:28 GMT</pubDate>
            <description><![CDATA[<h2 id="http와-https의-차이점">HTTP와 HTTPS의 차이점</h2>
<blockquote>
<p>참고링크</p>
</blockquote>
<ul>
<li><p><a href="https://brunch.co.kr/@hyoi0303/10">https://brunch.co.kr/@hyoi0303/10</a></p>
</li>
<li><p><a href="https://opentutorials.org/course/228/4894">https://opentutorials.org/course/228/4894</a></p>
<p>HTTP 프로토콜의 문제점은 서버에서부터 브라우저로 전송되는 정보가 암호화되지 않는다는 것이다.
즉, 데이터가 쉽게 도난될 수 있다. HTTPS 프로토콜은 SSL(보안 소켓 계층)을 사용하여 이 문제를 해결했다.
SSL은 서버와 브라우저 사이에 안전하게 암호화된 연결을 만들어 준다.
SSL 인증서는 사용자가 사이트에 제공하는 정보를 암호화한다. 따라서 누가 데이터를 도난하더라도
암호화된 데이터기 때문에 해독할 수 없다. 이 외에도 TLS 프로토콜을 통해서도 보안을 유지한다.
TSL는 데이터 무결성을 제공하기 때문에 데이터가 전송 중에 수정되거나 손상되는 것을 방지하고, 사용자가 자신이 의도하는 웹사이트와 통신하고 있음을 입증하는 인증 기능을 제공한다.</p>
</li>
</ul>
<h3 id="https와-ssl">HTTPS와 SSL</h3>
<p>HTTPS는 SSL 프로토콜 위에서 돌아가는 프로토콜이다.
<img src="https://velog.velcdn.com/images/jmsuper_97/post/0aa6b323-2f59-4ffc-b756-fb2cc1f98b0d/image.png" alt="">
TCP/IP 프로토콜 위에서 SSL이 동작하고, SSL 위에서 HTTP가 동작한다.
이때 SSL위에서 동작하는 HTTP를 HTTPS라고 부른다.
<br></p>
<h3 id="ssl-디지털-인증서">SSL 디지털 인증서</h3>
<p> 클라이언트와 서버간의 통신을 제3자가 보증해주는 전자화된 문서이다. 웹브라우저가 서버에 접속한 직후에 서버는 웹브라우저에게 인증서를 전달한다. 웹브라우저는 인증서를 보고 해당 서버가 자신이 접속하려고 했던 서버가 맞는지 확인하는 절차를 수행한다.</p>
<ul>
<li>통신 내용이 공격자에게 노출되는 것을 막을 수 있다.
이를 위해서는 암호화와 복호화가 필요하다. Key를 통해 암호화된 데이터를 복호화할 수 있다.</li>
<li>클라이언트가 접속하려는 서버가 신뢰 할 수 있는 서버인지를 판단한다.</li>
<li>통신 내용의 악의적인 변경을 방지할 수 있다.</li>
</ul>
<br>

<h3 id="대칭키">대칭키</h3>
<p>암호화를 하는 쪽과 복호화를 하는 쪽이 동일한 키를 가지고 있는 것을 대칭키라고 한다.
암호화 명령어 : <code>openssl enc -e -des3 -salt -in plaintext.txt -out ciphertext.bin;</code>
복호화 명령어 : <code>openssl enc -d -des3 -in ciphertext.bin -out plaintext2.txt;</code>
<code>openssl</code> : openssl이라는 프로그램을 이용해서 암호화 하겠다.
<code>enc -e -des3</code> : des3라는 방식으로 암호화를 하겠다.
<code>-in plaintext.txt -out ciphertext.bin</code> : plaintext.txt라는 파일을 가져와서 ciphertext.bib이라는 파일로 암호화 하겠다.</p>
<p> 개인컴퓨터에서 암호화, 복호화하는 것은 문제가 되지 않는다. 그러나 멀리 있는 상대에게 암호화된 파일을 전송할 경우, 상대방이 파일을 읽을 수 있도록 KEY를 함께 보내줘야한다. 하지만, KEY는 암호화되지 않기 때문에, KEY를 도난당해 암호화 되었던 파일이 도난될 수 있다.<br></p>
<br> 

<h3 id="공개키">공개키</h3>
<p>대칭키 방식을 개선한 방식이다. 공개키 방식은 KEY가 2개가 있다. A키로 암호화를 하면 B키로 복호화 할 수 있고, B키로 암호화하면 A키로 복호화 할 수 있는 방식이다. 이 방식에 착안하여 둘 중 하나는 비공개키이고, 다른 하나는 공개키로 지정한다.</p>
<p>공개키는 누구나 다운받을 수 있다. 그러나 공개키가 유출되더라도 공개키에 해당하는 비공개키를 가지고 있는 사람만이 암호화된 데이터를 복호화할 수 있다. 따라서 KEY를 배달할 때의 사고가 발생하지 않는다.</p>
<p>만약 비공개키를 가지고 있는 사람이 공개키를 가지고 있는 사람에게 데이터를 전송하면 어떨까? 공개키는 누구나 가지고 있기 때문에 모두가 복호화할 수 있지 않을까? 그렇다. 그런데, 비공개키로 데이터를 암호화하는 것은 암호화가 목적이 아니다. 공개키를 소유한 쪽에서 전송받은 데이터가 비공개키를 가지고 있던 쪽이 보낸 것이 맞는지 <code>인증</code>하기 위한 목적으로 암호화하는 것이다.<br></p>
<ul>
<li><p>실습
공개키를 이용해서 RSA라는 방식의 공개키를 만드는 명령어. 뒤에 숫자가 높을수록 안전하다.
하지만, 그만큼 높은 성능을 컴퓨팅파워를 요구한다.</p>
<p>비밀키 생성 : <code>openssl genrsa -out private.pem 1024;</code>
공개키 생성 : <code>openssl rsa -in private.pem -out public.pem -outform PEM -pubout;</code><br>
private.pem이라는 비밀키를 통해 public.pem이라는 공개키를 만든다는 의미이다.
공개키를 통해 파일 암호화 : <code>openssl rsautl -encrypt -inkey public.pem -pubin -in file.txt -out file.ssl;</code>
openssl을 이용해서 암호화를 하는데 public.pem이라는 파일을 사용한다는 의미이다. 공개키를 가지고있는 사람이 공개키를 통해 암호화를 하여 비공개키를 가지고 있는 사람에게 파일을 전송하기 위한 명령어이다.<br>
비밀키를 통한 파일 복호화 : <code>openssl rsautl -decrypt -inkey private.pem -in file.ssl -out decrypted.txt</code><br></p>
</li>
</ul>
<h3 id="ssl-인증서">SSL 인증서</h3>
<ul>
<li>클라이언트가 접속한 서버가 신뢰 할 수 있는 서버임을 보장한다.</li>
<li>SSL 통신에 사용할 공개키를 클라이언트에게 제공한다.</li>
</ul>
<h4 id="ca">CA</h4>
<p>  클라이언트가 접속한 서버가 클라이언트가 의도한 서버가 맞는지 보장하는 기관을 CA(Certificate Authority) 또는 Root Certificate라고 부른다. 이러한 CA는 브라우저 벤더들의 판단에 따라 CA리스트에 탑제된다.<br></p>
<h4 id="ssl-인증서의-내용">SSL 인증서의 내용</h4>
<ul>
<li>서비스의 정보(인증서를 발급한 CA, 서비스의 도메인 등등)
클라이언트가 접속하려는 서버가 해당 서버가 맞는지 확인하는 정보</li>
<li>서버 측 공개키(공개키의 내용, 공개키의 암호화 방법)
서버와 클라이언트가 암호화를 통해 데이터를 전송하기 위한 정보<br></li>
</ul>
<h4 id="ca를-브라우저는-알고-있다">CA를 브라우저는 알고 있다.</h4>
<p>  브라우저는 내부적으로 CA의 리스트를 미리 파악하고 있다. 즉, 브라우저의 소스코드 안에 CA의 리스트가 있다. 브라우저의 CA리스트에 포함된 CA만이 공인된 CA가 될 수 있다. 또한 각 CA의 공개키를 브라우저는 알고 있다.
<br></p>
<h3 id="ssl-인증서가-서비스를-보증하는-방법">SSL 인증서가 서비스를 보증하는 방법</h3>
<ol>
<li>웹 브라우저가 서버에 접속할 때 서버는 브라우저에게 인증서를 제공한다.</li>
<li>브라우저는 해당 인증서가 자신의 CA리스트에 있는지 확인한다.</li>
<li>확인이 되었다면, 브라우저가 갖고 있는 해당 CA의 공개키를 이용해 인증서를 복호화 한다.</li>
<li>복호화가 가능하다면, 인증서가 공인된 기관에 의해서 인증된 것이라는 의미이다.</li>
</ol>
<br>

<h3 id="ssl의-동작방법">SSL의 동작방법</h3>
<p>공개키 방식은 보안적인 측면에서 안정적이지만 높은 컴퓨팅파워를 요구하기 때문에 성능상에 문제가 있다. 이 때문에 SSL은 암호화된 데이터를 보내기 위해 공개키와 대칭키를 혼합해서 사용한다.</p>
<p>실제 데이터는 대칭키를 사용하며, 대칭키를 전송할 때는 공개키를 사용한다. 대칭키는 공개키보다 성능상 우위를 갖기 때문에 반드시 필요한 경우에만 공개키를 사용하고 나머지 경우에는 대칭키를 사용하는 것이다.</p>
<p>네트워크 통신에는 3단계로 이루어진다.
악수 -&gt; 전송 -&gt; 세션종료</p>
<h4 id="악수handshake">악수(handshake)</h4>
<ol>
<li><p>클라이언트가 서버에 접속한다.(client hello)</p>
<ul>
<li><p>클라이언트가 생성한 랜덤 데이터를 서버에게 전송한다.</p>
</li>
<li><p>클라이언트가 지원하는 암호화 방식을 서버쪽에 전송한다.(클라이언트와 서버가 지원하는 암호화 방식이 다를 수 있다.)</p>
</li>
<li><p>이미 SSL 핸드쉐이킹을 했다면 세션 아이디를 전송한다.</p>
<ol start="2">
<li>서버는 클라이언트에게 응답한다.(server hello)</li>
</ol>
</li>
<li><p>서버가 생성한 랜덤 데이터를 전송한다.</p>
</li>
<li><p>서버가 선택한 클라이언트의 암호화 방식을 선택해서 클라이언트에게 알려준다.</p>
</li>
<li><p>클라이언트에게 인증서를 전송한다.</p>
<ol start="3">
<li>클라이언트는 서버의 인증서가 CA리스트에 있는 지 확인한다.
클라이언트가 가지고 있는 해당 CA의 공개키를 이용해 서버가 전송한 인증서를 복호화한다. 복호화가 성공한다면, 해당 서버는 믿을 수 있는 대상임을 증명하는 것이다.</li>
</ol>
<p>인증서 안에는 서버가 생생한 공개키가 들어가 있다. 이때 클라이언트는 서버의 공개키를 획득한다. 클라이언트는 서버의 랜덤 데이터와 클라이언트의 랜덤 데이터를 조합하여 pre master secret라는 키를 생성한다.</p>
<p>이후 서버의 공개키를 이용하여 pre master secret라는 키를 암호화하여 서버에게 전송한다. 이 키는 대칭키로 사용된다. <strong>pre master secret은 절대 노출되어서는 안된다.</strong></p>
</li>
</ul>
<ol start="4">
<li>서버는 클라이언트가 전송한 pre master secret값을 자신의 비공개키로 복호화한다. 서버와 클라이언트는 모두 pre master secret을 가지고 있으며, 일련의 과정을 거쳐서 pre master secret을 master key 값으로 만든다.</li>
</ol>
<p>이를 이용해 최종적으로 session key를 생성한다. session key는 이후 데이터를 주고받을 때 데이터를 암호화하는 대칭키로 사용된다.<br></p>
<ol start="5">
<li>클라이언트와 서버는 핸드쉐이크 단계의 종료를 서로에게 알린다.</li>
</ol>
</li>
</ol>
<blockquote>
<p>요약</p>
</blockquote>
<ol>
<li>서버가 클라이언트에게 인증서를 전송</li>
<li>클라이언트는 자신의 랜덤 데이터와 서버의 랜덤 데이터를 조합하여 pre master secret라는 키 생성 </li>
<li>클라이언트는 서버의 공개키로 pre master secret을 암호화하여 서버에게 전송</li>
<li>서버는 자신의 비공개키로 pre master secret을 복호화함</li>
<li>이후 일련의 과정을 거쳐 서버와 클라이언트는 master secret을 얻게되고 이를 이용해 session key를 생성</li>
</ol>
<h4 id="세션전송">세션(전송)</h4>
<p> session key사용하여 전송할 데이터를 암호화하여 전송. 상대편은 session key를 이용해 데이터를 복호화한다.
 이러한 대칭키 방식을 사용하는 이유는 공개키가 컴퓨팅 파워를 요구하기 때문에 이를 해결하기 위함이다.</p>
<h4 id="세션종료">세션종료</h4>
<p> SSL 통신이 끝났음을 서로에게 알려준다. 이 때 통신에서 사용한 대칭키인 세션키를 폐기한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[스프링] 1. SQL & JDBC 프로그래밍 - SQL과 자바 개발환경 세팅]]></title>
            <link>https://velog.io/@jmsuper_97/Spring-%EB%B6%80%EC%8A%A4%ED%8A%B8%EC%BD%94%EC%8A%A4-%EC%9B%B9%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B3%BC%EC%A0%95-1.-SQL-JDBC-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</link>
            <guid>https://velog.io/@jmsuper_97/Spring-%EB%B6%80%EC%8A%A4%ED%8A%B8%EC%BD%94%EC%8A%A4-%EC%9B%B9%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B3%BC%EC%A0%95-1.-SQL-JDBC-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</guid>
            <pubDate>Tue, 11 Oct 2022 12:21:03 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>강의 링크
<a href="https://www.boostcourse.org/web326/joinLectures/28304?isDesc=false">https://www.boostcourse.org/web326/joinLectures/28304?isDesc=false</a></p>
</blockquote>
<h2 id="학습할-것">학습할 것</h2>
<ul>
<li>MySQL</li>
<li>SQL</li>
<li>개발환경 설정</li>
<li>Maven</li>
</ul>
<h2 id="mysql">MySQL</h2>
<ul>
<li>Mysql 5.7.21 community edition installer 
다운링크 : <a href="https://downloads.mysql.com/archives/installer/">https://downloads.mysql.com/archives/installer/</a></li>
<li>환경 변수란?
프로세스가 컴퓨터에서 동작하는 방식에 영향을 미치는 동적인 값들의 모임이다. 즉, OS가 가지고 있는 변수들이다. 설정 값이나 경로 값을 시스템에서 사용하기 쉽게 변수로 지정해놓은 것이다.
환경 변수에 대표적으로 <code>Path</code>가 있다. <code>Path</code>를 통해 cmd창에서 단순한 명령어만으로 해당 프로세스를 실행할 수 있다.일반적으로 윈도우에서 프로그램을 실행할 때는 해당 실행파일이 있는 디렉토리에 접근하여 실행시킨다.
그런데 매번 직접 해당 디렉토리까지 들어가서 혹은 경로를 입력하여 실행시키는 것은 매우 귀찮은 일이다. 시스템 환경 변수 <code>Path</code>는 이러한 경로를 변수로서 저장해두어 쉽게 접근할 수 있도록 한다.
<img src="https://velog.velcdn.com/images/jmsuper_97/post/4bf47b6a-6a36-457a-9f3c-cd23237ed594/image.png" alt=""></li>
</ul>
<br>

<h2 id="sql">SQL</h2>
<ul>
<li>SQL은 데이터를 보다 쉽게 검색하고 추가, 삭제, 수정 같은 조작을 할 수 있도록 고안된 컴퓨터 언어이다.</li>
<li>관계형 데이터베이스에서 데이터를 조작하고 쿼리하는 표준 수단이다.</li>
<li>DML(Data Manipulation Language) : 데이터를 조작하기 위해 사용한다. INSERT, UPDATE, DELETE, SELECT 등이 여기에 해당한다.</li>
<li>DDL(Data Definition Language) : 데이터베이스의 스키마를 정의 하거나 조작하기 위해 사용한다. CREATE, DROP, ALTER 등이 여기에 해당한다.</li>
<li>DCL(Data Control Language) : 데이터를 제어하는 언어이다. 권한을 관리하고, 데이터의 보안, 무결성등을 정의한다. GRANT, REVOKE 등이 여기에 해당한다.</li>
</ul>
<br>

<h3 id="데이터베이스">데이터베이스</h3>
<h4 id="데이터베이스-생성하기">데이터베이스 생성하기</h4>
<p><code>create database connectdb</code></p>
<h4 id="데이터베이스-사용자-생성과-권한-주기">데이터베이스 사용자 생성과 권한 주기</h4>
<ul>
<li>Database를 생성했다면, 해당 데이터베이스를 사용하는 계정을 생성해야 한다. 또한, 해당 계정이 데이터베이스를 이용할 수 있는 권한을 줘야 한다. 다음와 같은 명령을 이용해서 사용자 생성과 권한을 줄 수 있다. db이름 뒤의 <code>*</code> 는 모든 권한을 의미한다. <code>@&#39;%&#39;</code> 는 어떤 클라이언트에서든 접근가능하다는 의미이고, <code>@&#39;localhost&#39;</code>는 해당 컴퓨터에서만 접근가능하다는 의미이다.</li>
<li><code>flush privileges</code>는 DBMS에게 적용을 하라는 의미이다. 해당 명령을 반드시 실행해줘야 한다.</li>
<li><code>사용자 계정 : connectuser, 암호 : connect123!@#</code> 인 경우의 grant 명령어 이다.
<code>grant all privileges on connectdb.* to connectuser@&#39;%&#39;identified by &#39;connect123!@#&#39;;</code>
<code>flush privilegees;</code></li>
</ul>
<h4 id="생성한-db에-접속하기">생성한 db에 접속하기</h4>
<p><code>mysql -h127.0.0.1 -uconnectuser -p connectdb</code></p>
<h4 id="mysql-연결-끊기">MYSQL 연결 끊기</h4>
<p><code>quit</code></p>
<h4 id="mysql-버전과-현재-날짜-구하기">MYSQL 버전과 현재 날짜 구하기</h4>
<p><code>SELECT VERSION(), CURRENT_DATE;</code></p>
<h4 id="쿼리를-이용해서-계산식의-결과도-구할-수-있다">쿼리를 이용해서 계산식의 결과도 구할 수 있다.</h4>
<p><code>SELECT SIN(PI()/4), (4+1)*5</code></p>
<br>

<h3 id="테이블">테이블</h3>
<h4 id="테이블의-구성요소">테이블의 구성요소</h4>
<ul>
<li>테이블 : RDBMS에서 기본이 되는 저장구조이다. 한 개 이상의 column과 0개 이상의 row로 구성된다.</li>
<li>열 : 테이블 상에서의 단일 종류의 데이터를 나타낸다. 특정 데이터 타입 및 크기를 가지고 있다.</li>
<li>행 : Column들의 값의 조합이다. 레코드라고 불린다. 기본키에 의해 구분되며 기본키는 중복될 수 없다.</li>
<li>필드 : 행과 열의 교차점으로, 데이터를 포함할 수 있고 없을 때는 NULL을 가질 수 있다.</li>
</ul>
<h4 id="examplessql-파일을-가져와-데이터베이스에-저장하기">examples.sql 파일을 가져와 데이터베이스에 저장하기</h4>
<p>cmd창에서 해당 파일의 경로로 들어간다. 이후 아래 명령어를 실행한다.
<code>mysql -uconnectuser -p connectdb &lt; examples.sql</code></p>
<h4 id="컬럼의-합성concatenation---concat">컬럼의 합성(Concatenation) - concat</h4>
<p><code>SELECT concat(컬럼명,&#39;-&#39;,컬럼명) AS &#39;별명&#39; FROM 테이블명</code></p>
<h4 id="중복행의-제거distinct---distinct">중복행의 제거(Distinct) - distinct</h4>
<p><code>SELECT distinct(컬럼명) from 테이블명</code></p>
<h4 id="문자열-자르기---substring">문자열 자르기 - substring</h4>
<p><code>SELECT substring(&#39;Happy Day&#39;,3,2)</code> =&gt; pp
mysql과 같은 DB에서 인덱스번호는 0이 아닌 1부터 시작한다
따라서 위 쿼리는 해당 문장의 3번 인덱스부터 다음 2개의 문자를 선택하라는 의미이다.<br></p>
<h4 id="비어있는-공백-채우기---lpadrpad">비어있는 공백 채우기 - LPAD,RPAD</h4>
<p>왼쪽 공백 채우기 : <code>SELECT LPAD(&#39;hi&#39;,5,&#39;?&#39;)</code> =&gt; ???hi
오른쪽 공백 채우기 : &#39;SELECT RPAD(&#39;joe&#39;,5,&#39;?&#39;)&#39; =&gt; joe??</p>
<h4 id="공백-지우기---trim">공백 지우기 - TRIM</h4>
<p><code>SELECT LTRIM(&#39; hello &#39;)</code> =&gt; &#39;hello &#39;<br>
<code>SELECT TRIM(BOTH &#39;x&#39; FROM &#39;xxxhixxx&#39;)</code> =&gt; hi<br></p>
<h4 id="cast-형변환---cast-convert">CAST 형변환 - cast, convert</h4>
<p>cast 함수는 type을 변경하는데 사용된다.
cast 함수의 사용법 : <code>CAST(expression AS type)</code> or <code>CONVERT(expression,type)</code></p>
<ul>
<li><code>SELECT cast(now() as date)</code> =&gt; 2021-11-12</li>
<li><code>SELECT convert(now(),date)</code> =&gt; 2021-11-12</li>
</ul>
<h4 id="그룹함수와-groupby절">그룹함수와 groupby절</h4>
<p><code>SELECT deptno, AVG(salary), SUM(salary) FROM employee group by deptno</code>
group by절을 사용할 때 그룹핑의 기준이 되는 컬럼을 select절에 추가해야한다.
그룹핑과 상관없는 컬럼을 넣을 경우 정상적으로 출력되지만, 올바를 결과가 아니므로, 반드시 group by에서 사용하는 컬럼명을 select절에 넣어야 한다.<br></p>
<br>

<h2 id="개발환경-설정">개발환경 설정</h2>
<h3 id="jdk--jre">JDK &amp; JRE</h3>
<ul>
<li>자바언어로 작성된 프로그램을 실행하기 위해서는 JRE(Java SE Runtime Environment)가 필요하다.</li>
<li>만약 자바언어로 개발하는 개발자가 아니라, 자바로 만들어진 프로그램을 사용하는 사용자라면 JRE만 설치하면 된다.</li>
<li>사용자 입장에서 자바를 설치하는다는 것은 보통 JRE를 설치하는 것을 말한다.</li>
<li>자바를 사용하는 개발자는 자바로 작성된 소스를 컴파일하고 관리할 필요가 있다.</li>
<li>이때 사용하는 도구를 JDK(Java SE Development Kit)라고 말한다.</li>
<li>JDK안에는 JRE도 포함되어 있다(JDK &gt; JRE). 왜냐하면 컴파일된 결과를 실행하기 위해서 JRE가 필요하기 때문이다.<blockquote>
<h4 id="jdk가-운영체제별로-설치파일을-제공하는-이유는-무엇일까">JDK가 운영체제별로 설치파일을 제공하는 이유는 무엇일까?</h4>
<p>자바 프로그램은 플랫폼에 구속받지 않고 JVM위에서 구동된다. 즉, 실행 환경에 구속받지 않고 실행가능하다는 장점을 갖는다.
하지만, 자바 프로그램이 구동되는 JVM은 OS에 종속적이기 때문에 각각의 OS에 맞게 JVM을 포함한 JDK는 운영체제 별로 설치 파일이 제공되어야 한다.<br></p>
</blockquote>
</li>
</ul>
<h3 id="java-환경설정">JAVA 환경설정</h3>
<p>JDK설치가 완료되면, JDK에 대한 시스템 환경설정을 수행해야 한다.
설정해야 할 환경변수의 이름은 다음과 같다.</p>
<ul>
<li>JAVA_HOME : JAVA가 설치된 경로를 지정</li>
<li>CLASSPATH : JAVA클래스가 있는 경로들을 지정</li>
<li>PATH : JAVA 실행파일이 있는 경로를 추가<br>

</li>
</ul>
<blockquote>
<h4 id="생각해보기">생각해보기</h4>
</blockquote>
<ol>
<li>자바로 작성된 프로그램을 실행하려면 JRE만 설치하면 됩니다. 이때는 환경변수를 설정할 필요가 없습니다.</li>
<li>그런데, JDK를 설치할 때는 환경변수를 설정해야 합니다. 환경변수를 설정하는 이유는 무엇일까요?
환경변수를 설정하지 않는다면, 콘솔창에서 자바를 실행할 때 매번 JDK가 있는 폴더에 들어가서 실행할 수 있다. 이러한 번거로움을 없애기 위해 JDK의 경로를 환경변수로 설정하는 것이다.</li>
<li>현재 설치된 JDK보다 높은 버전의 JDK를 설치했습니다. 이때 수정해야 할 환경변수는 무엇일까요?
JAVA_HOME만 수정하면된다. 왜냐하면, 다른 환경변수들이 JAVA_HOME을 참조하고 있기 떄문이다.
(하지만, 필자의 경우 위 방식으로 했을 때 콘솔창에서 javac가 실행되지 않아 절대경로를 환경변수로 등록하였다.)</li>
</ol>
<blockquote>
<p>자바 코딩 규칙 링크</p>
</blockquote>
<ul>
<li><a href="https://google.github.io/styleguide/javaguide.html">https://google.github.io/styleguide/javaguide.html</a><br></li>
<li><a href="https://myeonguni.tistory.com/1596">https://myeonguni.tistory.com/1596</a><br></li>
<li><a href="http://kwangshin.pe.kr/blog/java-code-conventions-%EC%9E%90%EB%B0%94-%EC%BD%94%EB%94%A9-%EA%B7%9C%EC%B9%99/">http://kwangshin.pe.kr/blog/java-code-conventions-%EC%9E%90%EB%B0%94-%EC%BD%94%EB%94%A9-%EA%B7%9C%EC%B9%99/</a><br></li>
</ul>
<br>

<h2 id="maven">Maven</h2>
<blockquote>
<p>참고링크 : <a href="https://junghn.tistory.com/entry/SPRINGMaven-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-groupId-artifactId-version-%EC%9D%B4%EB%9E%80">https://junghn.tistory.com/entry/SPRINGMaven-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-groupId-artifactId-version-%EC%9D%B4%EB%9E%80</a></p>
</blockquote>
<h3 id="maven의-정의">Maven의 정의</h3>
<ul>
<li>애플리케이션 개발을 위해 반복적으로 진행해왔던 작업들을 지원하기 위한 도구</li>
<li>빌드, 패키징, 문서화, 테스트와 테스트 리포팅, git, 의존성관리, svn등과 같은 형상관리서버와 연동 및 배포 등의 작업을 손쉽게 할 수 있다.</li>
<li>Maven을 이해하기 위한 Coc
Coc : 일종의 관습을 말한다. 프로그램의 소스파일은 어떤 위치에 있어야 하고, 소스가 컴파일된 파일들은 어떤 위치에 있어야 하고 등을 미리 정해놓은 것.</li>
<li>Maven을 사용한다는 것은 Coc를 알아가는 것이라고 말할 수 있다.</li>
</ul>
<h3 id="maven의-이점">Maven의 이점</h3>
<ul>
<li>의존성 라이브러리 관리</li>
<li>설정 파일에 몇 줄 적어줌으로써 직접 다운로드 받지 않아도 라이브러리를 사용 가능</li>
<li>Maven을 사용하게 되면 Maven에 설정한 대로 모든 개발자가 일관된 방식으로 빌드를 수행 가능</li>
<li>또한 다양한 플러그인을 제공하여, 많은 일들을 자동화 가능</li>
</ul>
<h3 id="maven-기본">Maven 기본</h3>
<p>Archetype을 이용하여 Maven기반 프로젝트를 생성하면, 프로젝트 하위에 pom.xml파일이 생성됨</p>
<pre><code>&lt;project xmlns=&quot;http://maven.apache.org/POM/4.0.0&quot;
    xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
    xsi:schemaLocation=&quot;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd&quot;&gt;
    &lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;
    &lt;groupId&gt;kr.or.connect&lt;/groupId&gt;
    &lt;artifactId&gt;examples&lt;/artifactId&gt;
    &lt;packaging&gt;jar&lt;/packaging&gt;
    &lt;version&gt;1.0-SNAPSHOT&lt;/version&gt;
    &lt;name&gt;mysample&lt;/name&gt;
    &lt;url&gt;http://maven.apache.org&lt;/url&gt;
    &lt;dependencies&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;junit&lt;/groupId&gt;
            &lt;artifactId&gt;junit&lt;/artifactId&gt;
            &lt;version&gt;3.8.1&lt;/version&gt;
            &lt;scope&gt;test&lt;/scope&gt;
        &lt;/dependency&gt;
    &lt;/dependencies&gt;
&lt;/project&gt;</code></pre><ul>
<li>project : pom.xml 파일의 최상위 루트 엘리먼트</li>
<li>modelVersion : POM model의 버전</li>
<li>groupId : 프로젝트를 생성하는 조직의 고유 아이디를 결정. 일반적으로 도메인 이름을 거꾸로 적은 것</li>
<li>artifacId : 버전 없는 jar파일의 이름.</li>
<li>packaging : 해당 프로젝트를 어떤 형태로 packaging할 것인지 정함. jar, war,ear 등</li>
<li>version : 프로젝트의 현재 버전. 개발 중인 프로젝트는 SNAPSHOT 접미사를 사용함.</li>
<li>name : 프로젝트의 이름</li>
<li>url : 프로젝트 사이트가 있을 경우 URL을 등록함</li>
<li>dependencies : Dependency Management 기능의 핵심. 해당 엘리먼트 안에 필요한 라이브러리를 지정함.</li>
</ul>
<p><br><br></p>
<h2 id="이클립스">이클립스</h2>
<blockquote>
<p>참고링크 : <a href="https://devsnote.com/writings/2185">https://devsnote.com/writings/2185</a></p>
</blockquote>
<h3 id="eclipse">eclipse</h3>
<p>JAVA를 비롯한 다양한 프로그래밍 언어를 개발할 수 있는 통합 개발 환경(IDE).
이클립스는 플러그인 구조로 쉽게 기능을 추가할 수 있는 구조로 되어 있다.</p>
<h3 id="ide">IDE</h3>
<p>코딩, 디버그, 컴파일, 배포 등 프로그램 개발에 관련된 모든 작업을 하나의 프로그램 안에서 처리할 수 있도록 환경을 제공하는 소프트웨어
<br></p>
<h3 id="생각해보기-1">생각해보기</h3>
<ol>
<li><p>이클립스는 자바로 만들어진 프로그램입니다. 이클립스가 실행되기 위해서 설치되어 있어야 할 프로그램은 무엇일까요?<br>
자바로 만들어진 프로그램을 실행하기 위해서, JRE이 설치되어 있어야 한다.<br></p>
</li>
<li><p>이클립스에 모든 플러그인을 제거하면 빈 윈도우만 남는다고 하였습니다. 플러그인을 제작할 수 있다면, 이 빈 윈도우에 플러그인을 채워 넣을 수 있을 것입니다. 이런 방식으로 개발하는 것을 이클립스 RCP(Rich Client Platform)이라고 말합니다. 이클립스 RCP를 이용하여 만들어진 소프트웨어에는 어떤 것들이 있을까요?<br></p>
</li>
</ol>
<ul>
<li>Object Aid UML Diagram : UML 다이어그램을 그리는 툴<br></li>
<li>CheckStyle : 특정 코딩 표준을 준수하는지 확인하는 유효성 검사 도구<br></li>
<li>Project Lombok : 자주 쓰이는 코드를 줄이는 것을 목표로 코드 생성을 자동화 하는 도구<br></li>
<li>Maven : POM(프로젝트 객체 모델) 개념으로 프로젝트 정보를 한군데에서 관리하도록 함<br></li>
<li>Gradle : Maven과 비슷한 역할을 하지만 JSON 형태로 빌드를 관리해주는 도구<br></li>
<li>Jacoco : 단위 테스트의 측정 도구<br></li>
</ul>
<h4 id="느낀점">느낀점</h4>
<p>vscode에서 flutter와 nodejs로 개발할때는 특정 도구 없이 날 것으로 코딩했었는데위와 같은 도구들이 있다니... 어메이징하다.<br>
반복작업으로 신물이 났었는데 도구들을 사용해서 더 생산성있는 개발자가 될 수 있을 것 같다.<br></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] 람다]]></title>
            <link>https://velog.io/@jmsuper_97/Java-%EB%9E%8C%EB%8B%A4</link>
            <guid>https://velog.io/@jmsuper_97/Java-%EB%9E%8C%EB%8B%A4</guid>
            <pubDate>Tue, 04 Oct 2022 13:46:10 GMT</pubDate>
            <description><![CDATA[<h2 id="학습할-것-필수"><strong>학습할 것 (필수)</strong></h2>
<ul>
<li>람다식 사용법</li>
<li>함수형 인터페이스</li>
<li>Variable Capture</li>
<li>메소드, 생성자 레퍼런스</li>
</ul>
<blockquote>
<p>참고자료
    - <a href="https://www.geeksforgeeks.org/lambda-expressions-java-8/">https://www.geeksforgeeks.org/lambda-expressions-java-8/</a>
    - <a href="https://www.javatpoint.com/java-lambda-expressions">https://www.javatpoint.com/java-lambda-expressions</a>
    - <a href="https://livebook.manning.com/book/java-8-in-action/appendix-d/41">https://livebook.manning.com/book/java-8-in-action/appendix-d/41</a>
    - <a href="http://cr.openjdk.java.net/~briangoetz/lambda/lambda-translation.html">http://cr.openjdk.java.net/~briangoetz/lambda/lambda-translation.html</a>
    - <a href="https://www.geeksforgeeks.org/java-lambda-expression-variable-capturing-with-examples/">https://www.geeksforgeeks.org/java-lambda-expression-variable-capturing-with-examples/</a>
    - <a href="https://velog.io/@sdb016/Variable-Capture">https://velog.io/@sdb016/Variable-Capture</a></p>
</blockquote>
<p><br><br></p>
<h2 id="람다식">람다식</h2>
<p>람다식은 자바8에 도입된 ‘하나의 메소드를 가진 인터페이스’를 쉽게 나타낼 수 있는 방법이다. 람다식은 특히 <code>collection</code> 라이브러리에서 유용하다. <code>collection</code> 에서 반복, 필터링, 데이터 추출을 도와준다.</p>
<p>람다식은 함수형 인터페이스를 가지고 있는 인터페이스를 구현하는데 사용된다. 람다식을 사용하므로서 많은 코드를 줄일 수 있다.</p>
<pre><code class="language-java">public class EX1{
    public static void main(String[] args) {

        int width = 10;
                // 람다식을 사용하지 않을 경우
        Drawable d = new Drawable() {
            @Override
            public void draw() {
                System.out.println(&quot;Drawing &quot; + width);
            }
        };
        d.draw();

        // 람다식을 사용할 경우
        Drawable d2 = ()-&gt; System.out.println(&quot;Drawing &quot; + width);
        d2.draw();
    }
}

interface Drawable{
    public void draw();
}</code></pre>
<br>

<h3 id="람다식의-장점">람다식의 장점</h3>
<ul>
<li>함수형 프로그래밍을 가능케 한다.</li>
<li>코드를 줄임으로써 읽기 쉽고 간결한 코드</li>
<li>람다식을 사용하도록 고안된 API는 사용하기 편리하다</li>
<li>병렬 프로세싱을 도와준다.(Thread의 Runnable 인터페이스)</li>
</ul>
<br>

<h3 id="익명클래스는-무엇인가">익명클래스는 무엇인가?</h3>
<pre><code class="language-java">public class EX4_anonymous_class {

    public static Animal cat = new Animal(){
        @Override
        public void eat(){
            System.out.println(&quot;eat fish!&quot;);
        }
    };

    public static void main(String[] args) {
        cat.eat();
        Animal dog = new Animal(){
            @Override
            public void eat(){
                System.out.println(&quot;eat meat!&quot;);
            }
        };
        dog.eat();

        Bug bug = new Bug() {
            @Override
            public void eat() {
                System.out.println(&quot;eat bug!&quot;);
            }
        };
        bug.eat();
    }
}

class Animal{
    public void eat(){
        System.out.println(&quot;eat!&quot;);
    }
}

interface Bug{
    void eat();
}</code></pre>
<p>익명 클래스란 이름이 없는 클래스를 말한다. 이름이 없기 때문에 단발적으로만 사용할 때 정의한다. 익명 클래스를 구현하는 방식은 두 가지 이다. 첫 번째는 부모 클래스를 상속하는 자식 클래스를 익명 클래스로 정의하는 것이며, 두 번째는 인터페이스를 구현하는 것이다. 위 예제에서 <code>Animal</code> 이 전자이고, <code>Bug</code> 가 후자이다.</p>
<p><br><br></p>
<h2 id="람다식-사용법">람다식 사용법</h2>
<pre><code class="language-java">(argument-list) -&gt; {body}</code></pre>
<p>람다식을 세 가지의 컴포넌트를 갖는다.</p>
<ul>
<li>argument-list : 비어있거나, 여러 개를 가질 수 있다.</li>
<li>arrow-token : argument-list 와 body를 연결짓는데 사용된다.</li>
<li>body : 람다식의 실행문을 담는다.</li>
</ul>
<p>람다식은 반드시 함수형 인터페이스를 구현하는 방식으로 사용 가능하다. 즉, 람다식을 통해 함수를 만들었다고 해도, 해당 함수는 추상 메소드를 구현하는 방식으로 사용되어야 한다.</p>
<pre><code class="language-java">public class EX4_lambda_use {
    public static void main(String[] args) {
        Calculate c1 = (a,b)-&gt; a+b;
        Calculate c2 = (a,b) -&gt;{return a+b;};
        Calculate c3 = Integer::sum;

        System.out.println(c1.calculate(10,20));
        System.out.println(c2.calculate(10,20));
        System.out.println(c3.calculate(10,20));
    }
}

interface Calculate{
    int calculate(int a, int b);
}</code></pre>
<p>c1,c2,c3 는 모두 동일한 동작을 수행한다. 셋 다 람다식으로 표현한 것이다. 이 중 <code>Calculate c3 = Integer::sum</code> 이 의아해 보일 수 있다. 나도 처음에 저게 뭔가 싶었다. 바이트 코드를 살펴보니</p>
<pre><code class="language-java">INVOKEDYNAMIC calculate()Lcom/example/Calculate; [
      // handle kind 0x6 : INVOKESTATIC
      java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
      // arguments:
      (II)I, 
      // handle kind 0x6 : INVOKESTATIC
      java/lang/Integer.sum(II)I, 
      (II)I
    ]</code></pre>
<p>일반적인 람다식의 바이트코드와 마찬가지의 형태를 보이지만, 추상 메소드를 호출할 때, 이번에 컴파일 하면서 생성한</p>
<pre><code class="language-java">private static synthetic lambda$main$0(II)I
   L0
    LINENUMBER 7 L0
    ILOAD 0
    ILOAD 1
    IADD
    IRETURN
   L1
    LOCALVARIABLE a I L0 L1 0
    LOCALVARIABLE b I L0 L1 1
    MAXSTACK = 2
    MAXLOCALS = 2</code></pre>
<p>위와 같은 static 메소드를 호출하지 않고, <code>java.lang.Integer::sum</code> 을 호출하고 있다. 함수형 인터페이스에 대해 람다식을 통해 구현하지 않고, 다른 클래스의 함수를 사용할 경우, 추상 메소드 호출 시 다른 클래스의 함수를 호출하는 것으로 유추된다.</p>
<br>

<h3 id="람다식-바이트코드">람다식 바이트코드</h3>
<pre><code class="language-java">public class EX5_lambda_byte_code {
    public static void main(String[] args) {
        Functional f = ()-&gt;{};
    }
}

interface Functional{
    void abstractMethod();
}</code></pre>
<p>람다식이 어떠한 방식으로 동작하는지 자세히 알기 위해 위와 같이 간단한 코드를 컴파일하였다.</p>
<pre><code class="language-java">public class com/example/EX5_lambda_byte_code {

  // compiled from: EX5_lambda_byte_code.java
  // access flags 0x19
  public final static INNERCLASS java/lang/invoke/MethodHandles$Lookup java/lang/invoke/MethodHandles Lookup

  // access flags 0x1
  public &lt;init&gt;()V
   L0
    LINENUMBER 3 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.&lt;init&gt; ()V
    RETURN
   L1
    LOCALVARIABLE this Lcom/example/EX5_lambda_byte_code; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x9
  public static main([Ljava/lang/String;)V
   L0
    LINENUMBER 5 L0
    INVOKEDYNAMIC abstractMethod()Lcom/example/Functional; [
      // handle kind 0x6 : INVOKESTATIC
      java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
      // arguments:
      ()V, 
      // handle kind 0x6 : INVOKESTATIC
      com/example/EX5_lambda_byte_code.lambda$main$0()V, 
      ()V
    ]
    ASTORE 1
   L1
    LINENUMBER 6 L1
    RETURN
   L2
    LOCALVARIABLE args [Ljava/lang/String; L0 L2 0
    LOCALVARIABLE f Lcom/example/Functional; L1 L2 1
    MAXSTACK = 1
    MAXLOCALS = 2

  // access flags 0x100A
  private static synthetic lambda$main$0()V
   L0
    LINENUMBER 5 L0
    RETURN
    MAXSTACK = 0
    MAXLOCALS = 0
}</code></pre>
<p>위 바이트코드에서 잘 모르겠는 부분은 다음과 같다. 하나씩 알아가 보겠다.</p>
<ul>
<li><p><code>INVOKEDYNAMIC abstractMethod()Lcom/example/Functional;</code>
  동적 메소드 호출을 위한 객체(CallSite)를 생성하기 위해 <code>invokespecial</code> 명령어를 사용한다.</p>
</li>
<li><p><code>java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang ...</code></p>
<p>  bootstrap 영역의 <code>lambdafactory.metafactory</code> 를 호출한다. 해당 메소드에서 <code>CallSite</code> 객체를 생성하는데, 어떤 방법으로 객체를 생성할지는 동적으로 결정된다.</p>
</li>
<li><p><code>private static synthetic lambda$main$0()V</code>
  람다식은 private static method로 생성된다.</p>
</li>
</ul>
<br>

<p><strong>Invokedynamic(INDY)</strong></p>
<p>INDY는 JVM이 동적 타입 언어를 지원하도록 하기위해 추가된 기능이다. 자바7에서 처음으로 릴리즈되었다. 비록 INDY가 동적 타입 언어를 지원하지만, 동적인 형태를 필요로 하는 언어에도 도움을 준다. 자바는 정적 타입 언어인데도 불구하고 자바8의 람다식은 INDY를 사용하여 구현되었다.</p>
<p>JVM은 이전부터 4가지 메소드 호출 타입을 지원하였다.</p>
<ul>
<li><code>invokestatic</code> : 스태틱 메소드 호출</li>
<li><code>invokeinterface</code> : 인터페이스 메소드 호출</li>
<li><code>invokespecial</code> : 생성자 or super() or private 메소드 호출</li>
<li><code>invokevirtual</code> : 인스턴스 메소드 호출</li>
</ul>
<p>위 메소드 호출 타입은 컴파일 타임에 어떤 메소드가 호출되는지 알고 있어야 한다. 하지만, INDY는 컴파일 타임에 어떤 메소드가 호출될지 알 수 없으며, 런타임에 메소드가 정해진다.</p>
<p><img src="https://velog.velcdn.com/images/jmsuper_97/post/1e01600b-ff04-4b90-83df-ca9770dd6f8c/image.png" alt=""></p>
<p>JVM이  처음 <code>invokedynamic</code> 명령어를 만나게 되면, 부트스트랩 메소드를 호출한다. 해당 메소드는 <code>java.lang.invoke.CallSite</code> 의 인스턴스를 반환한다. <code>CallSite</code> 는 실제 메소드에 대한 래퍼런스를 가지고 있다. JVM이 <code>invokedynamic</code> 명령어를 다시 만나게 되면, 위 과정들을 생략하고 곧바로 target method를 호출한다. 다만, 어떤 변화가 없다는 가정하에 말이다.</p>
<br>

<p><strong>bootstrap method</strong></p>
<p><code>CallSite</code> 를 초기화하는 메소드이다. INDY는 JVM에서 임의의 함수를 호출하는데 사용되는 바이트코드 명령어이다. 컴파일 타임에는 어떤 함수가 호출되는 지 알 수 없다. 따라서, 특정 함수 대신에 <code>CallSite</code> 라는 클래스의 객체를 사용한다. </p>
<p><br><br></p>
<h2 id="함수형-인터페이스">함수형 인터페이스</h2>
<p>자바에서 함수가 독립적으로 존재할 수는 없다. 함수는 반드시 클래스나 인터페이스에 속하게 된다. 따라서, 함수를 호출하려면 해당 클래스나 객체를 사용해야 한다.</p>
<p>함수형 인터페이스는 함수를 독립적으로 나타내기 위해 고안되었다. 함수형 인터페이스는 하나의 추상 메소드를 가진 인터페이스를 말한다. 오직 하나의 추상 메소드만 가지고 있어야 한다. defalut 메소드는 여러 개 가지고 있어도 된다. 람다식은 이러한 함수형 인터페이스의 인스턴스를 나타내기 위해 등장하였다. 즉, 함수형 인터페이스와 람다식의 조합으로, 함수가 마치 독립적으로 존재하는 것처럼 사용할 수 있다.</p>
<br>

<h3 id="functionalinterface-어노테이션">@FunctionalInterface 어노테이션</h3>
<p>위 어노테이션은 함수형 인터페이스가 오직 하나의 추상 메소드만 갖도록 보장한다. 그러나 위 어노테이션을 의무적으로 사용해야 되는 것은 아니다.</p>
<p>자바8에서는 네 가지 주요한 종류의 함수형 인터페이스를 포함하고 있다. 아래 함수형 인터페이스들은 다양한 상황에 적용될 수 있다.</p>
<ul>
<li>Consumer</li>
<li>Predicate</li>
<li>Function</li>
<li>Supplier</li>
</ul>
<br>

<h3 id="consumer">Consumer</h3>
<p>Consumer 인터페이스는 하나의 아규먼트를 받고, 리턴은 하지 않는다. 말 그대로 매개변수를 소비만 하고 공급은 하지 않음을 의미한다.</p>
<pre><code class="language-java">Consumer&lt;String&gt; consumer = (value) -&gt; System.out.println(value);
consumer.accept(&quot;hello world&quot;);
// hello world</code></pre>
<p>람다식을 통해 함수형 인터페이스의 인스턴스를 생성한다. 람다식으로 표현된 함수는 <code>accept()</code> 를 구현한 것이며, <code>accept()</code> 를 호출하여 함수를 호출할 수 있다.</p>
<pre><code class="language-java">Consumer&lt;String&gt; consumer = (value) -&gt; System.out.println(value);
List&lt;String&gt; list = Arrays.asList(&quot;apple&quot;,&quot;banana&quot;,&quot;orange&quot;);
list.forEach(consumer);
// apple
// banana
// orange</code></pre>
<p>배열을 순회하며 각 엘리먼트에 대해 함수를 호출하고 싶을 때 함수형 인터페이스가 유용하게 사용된다.</p>
<pre><code class="language-java">Consumer&lt;StringBuilder&gt; print = (value) -&gt; System.out.println(value.toString());
Consumer&lt;StringBuilder&gt; addString = (value) -&gt; value.append(&quot; world&quot;);
addString.andThen(print).accept(new StringBuilder(&quot;hello&quot;));
// hello world</code></pre>
<p>Consumer의 default method인 <code>andthen</code> 을 사용하면, Consumer를 체이닝할 수 있다. 이때 처음 매개변수로 받은 value가 다음 Consumer로 전달된다. 다만, primitive type은 초기 값이 전달 되므로 유의해야 한다.</p>
<br>

<h3 id="predicate">Predicate</h3>
<p>Predicate는 매개변수를 받아 boolean 값을 반환한다. 즉, 매개변수를 보고 이에 대한 True or False를 결정하는 것이다. Predicate의 구현체는 filtering 로직에서 사용된다. </p>
<pre><code class="language-java">Predicate&lt;Integer&gt; predicate = (value) -&gt; value &gt;0;
List&lt;Integer&gt; list = Arrays.asList(-2,-1, 0, 1, 2);
list.stream().filter(predicate).forEach((value)-&gt; System.out.println(value));
// 1
// 2</code></pre>
<br>

<h3 id="function">Function</h3>
<p>Function은 하나의 매개변수를 받고 값을 반환한다. primitive type은 general한 타입의 매개변수로 받을 수 없기 때문에, 이를 지원하기 위해 Function에는 수많은 버전들이 존재한다. 필요에 따라 해당하는 인터페이스를 사용하면 된다.</p>
<pre><code class="language-java">Function&lt;String, String&gt; function = (value) -&gt; value + &quot; addValue&quot;;
System.out.println(function.apply(&quot;Default String&quot;));
// Default String addValue</code></pre>
<br>

<h3 id="supplier">Supplier</h3>
<p>Supplier는 매개변수를 받지 않고, 하나의 값만 반환한다. 공급은 하지만, 소비는 하지 않는다.</p>
<pre><code class="language-java">Supplier&lt;String&gt; supplierStr = () -&gt; &quot;supply String!&quot;;
System.out.println(supplierStr.get());</code></pre>
<p><br><br></p>
<h2 id="variable-capture">Variable Capture</h2>
<p>람다는 자유 변수를 참조할 때 직접 그 변수를 참조하지 않고, 자유 변수를 자신의 stack에 복사하여 참조한다. 이를 <code>variable capture</code> 라고 말한다.</p>
<p>람다식을 감싸고 있는 대상의 변수들에 대해 람다식에서 접근하는 것이 가능하다. 예를 들어, 람다식은 람다식의 감싸고 있는 클래스의 인스턴스와 스태틱 변수를 사용할 수 있다. 또한, 메소드를 호출할 수 있다.</p>
<p>그런데 만약, 람다식이 그것을 감싸고 있는 대상에 있는 로컬 변수를 사용하면 <code>variable capture</code> 와 관련한 문제가 발생할 수 있다. 이를 위해 람다식에서 사용하는 자유 변수는 아래 두 가지 제약 조건을 따라야 한다.</p>
<ol>
<li>자유 변수는 final로 선언되어야 한다.(Java8 이전)</li>
<li>final로 선언되지 않은 자유 변수는 final처럼 동작해야 한다.(effectively final)</li>
</ol>
<pre><code class="language-java">public class EX6_variable_capture {
    public static void main(String[] args) {
        int number = 10;
        MyFunction myLambda = (n)-&gt;{
            int value = number + n;
            return value;
        };
        number = 9;
        System.out.println(&quot;Test&quot;);
    }
}

interface MyFunction{
    int func(int n);
}
// 출력
// java: local variables referenced from a lambda expression must be final or effectively final</code></pre>
<p>위 코드는 제약조건을 어겼기 때문에 컴파일 에러가 발생한다.</p>
<pre><code class="language-java">public class EX7_variable_capture2 {

    int data = 170;

    public static void main(String[] args) {
        EX7_variable_capture2 test  = new EX7_variable_capture2();

        MyInterface myInterface = ()-&gt;{
            System.out.println(&quot;Data : &quot; + test.data);
            test.data += 500;
            System.out.println(&quot;Data : &quot; + test.data);
        };

        myInterface.myFunction();
        test.data += 200;
        System.out.println(&quot;Data : &quot; + test.data);
    }
}

interface MyInterface{
    void myFunction();
}

// Data : 170
// Data : 670
// Data : 870</code></pre>
<p>위 코드는 제약조건을 어긴 것처럼 보이지만, 정상적으로 동작한다. 왜냐하면 객체의 멤버 변수의 값이 수정되었지만, 객체를 가리키는 래퍼런스 변수는 수정되지 않았기 때문이다(effectively final). 만약, 래퍼런스 변수를 수정한다면 컴파일 에러가 발생할 것이다.</p>
<pre><code class="language-java">public class EX7_variable_capture_method_reference {
    public static void main(String[] args) {
        TestInterface retInterface = TestClass.func2();
        retInterface.func(10);
        TestClass.addData(20);
        retInterface.func(0);
    }
}

class TestClass{
    int data = 10;

    public static TestClass instance = new TestClass();

    public static TestInterface func2(){
        TestInterface testInterface = (n)-&gt; {
            instance.data += 10;
            System.out.println(&quot;Data : &quot; + instance.data);
        };
        testInterface.func(10);
        return testInterface;
    }

    public static void addData(int n){
        instance.data += 10;
    }
}

interface TestInterface{
    void func(int n);
}</code></pre>
<p>아래 바이트코드는 위 코드 중 람다식 부분이다. 람다식에서는 static 변수를 사용하고 있다. 이에 따라, 바이트코드에서도 클래스의 static 변수를 가져오는 것을 확인할 수 있었다.</p>
<pre><code class="language-java">private static synthetic lambda$func2$0(I)V
   L0
    LINENUMBER 20 L0
    GETSTATIC com/example/TestClass.instance : Lcom/example/TestClass;
    DUP
    GETFIELD com/example/TestClass.data : I
    BIPUSH 10
    IADD
    PUTFIELD com/example/TestClass.data : I</code></pre>
<br>

<blockquote>
<p><strong>variable capture 값은 어디에 저장될까?</strong>
그렇다면, capture된 값은 어디에 저장되는 것일까? 자유 변수를 람다식 내부에서 사용하면, 이후 람다식의 객체를 재사용할 경우 초기 자유 변수의 값이 유지되는 것을 알 수 있다. 마치 람다식이 구현한 인터페이스 객체에 final 변수가 있는 것처럼 말이다. 구글을 엄청 뒤져봤지만, variable capture된 값이 stack에 저장된다고만 있을 뿐, 람다식 재사용시 해당 값이 어디에 저장되는 지는 찾지 못했다. 그래서, 인터페이스의 구현체에 final로 담겨있다고 유추하였다.</p>
</blockquote>
<h2 id="메소드-생성자-레퍼런스">메소드, 생성자 레퍼런스</h2>
<p>이 부분은 위에 있는 예제 코드와 중복되어 생략한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] 제네릭]]></title>
            <link>https://velog.io/@jmsuper_97/Java-%EC%A0%9C%EB%84%A4%EB%A6%AD</link>
            <guid>https://velog.io/@jmsuper_97/Java-%EC%A0%9C%EB%84%A4%EB%A6%AD</guid>
            <pubDate>Mon, 03 Oct 2022 13:03:40 GMT</pubDate>
            <description><![CDATA[<h1 id="학습할-것-필수"><strong>학습할 것 (필수)</strong></h1>
<ul>
<li>제네릭 사용법</li>
<li>제네릭 주요 개념 (바운디드 타입, 와일드 카드)</li>
<li>제네릭 메소드 만들기</li>
<li>Erasure</li>
</ul>
<br>

<blockquote>
<p>참고자료</p>
</blockquote>
<ul>
<li><a href="http://www.tcpschool.com/java/java_generic_concept">http://www.tcpschool.com/java/java_generic_concept</a></li>
<li><a href="https://devlog-wjdrbs96.tistory.com/201">https://devlog-wjdrbs96.tistory.com/201</a></li>
</ul>
<p><br><br></p>
<h2 id="제너릭이란">제너릭이란?</h2>
<p>자바에서 제네릭이란 데이터의 타입을 일반화한다는 것을 의미한다. 제네릭은 클래스나 메소드에서 사용할 내부 데이터 타입을 컴파일 시에 미리 지정하는 방법이다. 컴파일에 미리 타입 검사를 수행하면 다음과 같은 장점을 얻을 수 있다.</p>
<ul>
<li>클래스나 메소드 내부에서 사용되는 객체의 타입 안정성을 높일 수 있다.</li>
<li>반환값에 대한 타입 변환 및 타입 검사에 들어가는 노력을 줄일 수 있다.</li>
</ul>
<p>JDK 1.5 이전에는 여러 타입을 사용하는 클래스나 메소드는 Object 타입을 사용하였다. Object를 사용하게 되면 Object 객체를 다시 원하는 타입으로 형 변환해줘야 한다. 그런데, 실수로 형 변환을 하지 않거나 다른 타입으로 변환할 수도 있다. 문제는 이런 오류는 런타임에 알 수 있다는 것이다.
<br>
제네릭을 사용하므로서, 컴파일 타임에 미리 체크할 수 있다.</p>
<pre><code class="language-java">public class BoxMain {
    public static void main(String[] args) {
        int value = 10;

        BoxObject boxObject = new BoxObject();
        boxObject.setElement(value);
        String object = (String) boxObject.getElement();

        BoxGeneric&lt;Integer&gt; boxGeneric = new BoxGeneric&lt;&gt;();
        boxGeneric.setElement(value);
        String object2 = boxGeneric.getElement();
    }
}

public class BoxObject {
    private Object element;
    public Object getElement() {
        return element;
    }
    public void setElement(Object element) {
        this.element = element;
    }
}

public class BoxGeneric&lt;T&gt; {
    private T element;
    public T getElement() {
        return element;
    }
    public void setElement(T element) {
        this.element = element;
    }
}

// 컴파일 에러
// java: incompatible types: java.lang.Integer cannot be converted to java.lang.String</code></pre>
<p><code>BoxObject</code> 에서는 <code>Object</code> 객체를 반환하기 때문에 컴파일 타임에 에러를 확인할 수 없다. 반면, <code>BoxGeneric</code> 은 <code>type argument</code> 로 받았던 <code>Integer</code> 를 반환하기 때문에 <code>String</code> 타입과 일치하지 않아, 컴파일 에러를 발생시킨다.</p>
<p><br><br></p>
<h2 id="제네릭-바이트-코드">제네릭 바이트 코드</h2>
<pre><code class="language-java">public class BiteCode &lt;T&gt;{
    private T t;
    public void setT(T t){
        this.t = t;
    }
    public T getT(){
        return t;
    }
    public static void main(String[] args) {
        BiteCode&lt;Integer&gt; biteCode = new BiteCode&lt;&gt;();
        biteCode.setT(10);
        Integer i = biteCode.getT();
    }
}</code></pre>
<p>제네릭 타입이 컴파일되었을 때 <code>type parameter</code> 는 어떻게 컴파일 될 지 궁금하였다. 그래서 바이트 코드를 확인하였다.</p>
<pre><code class="language-java">public class com/jm/test/BiteCode {

  private Ljava/lang/Object; t

  public &lt;init&gt;()V
   L0
    LINENUMBER 3 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.&lt;init&gt; ()V
    RETURN
   L1
    LOCALVARIABLE this Lcom/jm/test/BiteCode; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

  public setT(Ljava/lang/Object;)V
   L0
    LINENUMBER 6 L0
    ALOAD 0
    ALOAD 1
    PUTFIELD com/jm/test/BiteCode.t : Ljava/lang/Object;
   L1
    LINENUMBER 7 L1
    RETURN
   L2
    LOCALVARIABLE this Lcom/jm/test/BiteCode; L0 L2 0
    LOCALVARIABLE t Ljava/lang/Object; L0 L2 1
    MAXSTACK = 2
    MAXLOCALS = 2

  public getT()Ljava/lang/Object;
   L0
    LINENUMBER 9 L0
    ALOAD 0
    GETFIELD com/jm/test/BiteCode.t : Ljava/lang/Object;
    ARETURN
   L1
    LOCALVARIABLE this Lcom/jm/test/BiteCode; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

  public static main([Ljava/lang/String;)V
   L0
    LINENUMBER 12 L0
    NEW com/jm/test/BiteCode
    DUP
    INVOKESPECIAL com/jm/test/BiteCode.&lt;init&gt; ()V
    ASTORE 1
   L1
    LINENUMBER 13 L1
    ALOAD 1
    BIPUSH 10
    INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
    INVOKEVIRTUAL com/jm/test/BiteCode.setT (Ljava/lang/Object;)V
   L2
    LINENUMBER 14 L2
    ALOAD 1
    INVOKEVIRTUAL com/jm/test/BiteCode.getT ()Ljava/lang/Object;
    CHECKCAST java/lang/Integer
    ASTORE 2
   L3
    LINENUMBER 15 L3
    RETURN
   L4
    LOCALVARIABLE args [Ljava/lang/String; L0 L4 0
    LOCALVARIABLE biteCode Lcom/jm/test/BiteCode; L1 L4 1
    LOCALVARIABLE i Ljava/lang/Integer; L3 L4 2
    MAXSTACK = 2
    MAXLOCALS = 3
}</code></pre>
<p><code>T</code> 는 <code>Object</code> 로 컴파일되었다. 이렇게 컴파일되면 제네릭을 사용하지 않고, <code>Object</code> 를 사용하는 것과 어떤 차이가 있는 것일까? 핵심은 <code>CHECKCAST</code> 에 있다.</p>
<pre><code class="language-java">Integer i = biteCode.getT();

CHECKCAST java/lang/Integer</code></pre>
<p><code>BiteCode</code> 의 인스턴스에서 <code>t</code> 를 반환한 뒤, 해당 값이 <code>Integer i</code> 에 할당될 때, 컴파일러는 자동으로 <code>getT</code> 가 반환한 값이 <code>Integer</code> 인지 타입 확인을 수행한다. 즉, Heap영역에 존재할 때는 Object 타입으로 존재하지만, 실제로 해당 값이 사용될 때 타입 확인을 수행하는 것이다. 자바 컴파일러가 수행하는 타입 확인을 명시적으로 나타낸다면 다음과 같을 것이다.</p>
<pre><code class="language-java">        BiteCode&lt;Object&gt; biteCode = new BiteCode&lt;&gt;();
        biteCode.setT(10);
        Integer i = (Integer)biteCode.getT();</code></pre>
<p>제네릭은 Object를 형변환하고 타입을 확인하는 일련의 과정을 컴파일러에서 자동으로 추가해주는 기법이라고 볼 수 있다.</p>
<p><br><br></p>
<h2 id="제네릭-사용법">제네릭 사용법</h2>
<p>제네릭에서 <code>Type parameter</code> 는 다음과 같은 convention을 따른다.</p>
<ul>
<li>한 글자</li>
<li>대문자</li>
</ul>
<p>대표적인 type parameter name은 다음과 같다.</p>
<ul>
<li>E - element</li>
<li>K - key</li>
<li>T - type</li>
<li>V - value</li>
<li>S,U,V etc. - 2nd, 3rd, 4th types</li>
</ul>
<p>일반 변수의 name convention과 다르기 때문에 일반 변수와 type parameter를 쉽게 구분할 수 있게 함이다.</p>
<br>

<h3 id="제네릭-type을-호출하고-초기화하는-법">제네릭 type을 호출하고 초기화하는 법</h3>
<pre><code class="language-java">BoxGeneric&lt;Integer&gt; boxGeneric = new BoxGeneric&lt;&gt;();</code></pre>
<p>위 코드는 BoxGeneric 클래스의 인스턴스를 생성하기 위한 코드이다. <code>T</code> 가 위치하던 자리에 <code>Integer</code> 가 들어가있다. 이때 <code>Integer</code> 를 <code>type argument</code> 라고 하며, <code>T</code> 는 <code>type parameter</code> 라고 말한다.</p>
<br>

<h3 id="the-diamond">The Diamond</h3>
<p>자바 7부터 제네릭 클래스의 생성자를 호출하기 위해 사용했던 <code>type argument</code> 를 <code>&lt;&gt;</code> 로 대체할 수 있다. 컴파일러가 context를 확인하여 Type을 결정하게 된다.</p>
<br>

<h3 id="multiple-type-parameters">Multiple Type Parameters</h3>
<p>제네릭 클래스는 여러 개의 <code>type parameter</code> 를 받을 수 있다.</p>
<pre><code class="language-java">public class Pair&lt;K, V&gt;{
        private K key;
        private V value;

        public Pair(K key, V value){
                this.key = key;
                this.value = value;
    }

        public K getKey(){return key;}
        public V getValue(){return value;}
}

Pair&lt;String, Integer&gt; p1 = new Pair&lt;String,Integer&gt;(&quot;Even&quot;,8);
Pair&lt;String, String&gt;  p2 = new Pair&lt;String,String&gt;(&quot;hello&quot;,&quot;world&quot;); </code></pre>
<p>K, V는 서로 다른 타입일 수 있고, 같은 타입일 수 있다. <code>type argument</code> 는 <code>primitive type</code> 이여도, 자바의 <code>autoboxing</code> 덕분에 <code>Wrapper class</code> 로 변환되어 정상적으로 컴파일 된다.</p>
<br>

<ul>
<li><p>autoboxing</p>
<p>  <code>primitive type</code> 의 값을 해당 타입과 대응되는 <code>wrapper class</code> 로 변환해주는 것을 <code>autoboxing</code> 이라고 말한다.  autoboxing은 다음 조건에서 수행된다.</p>
<ul>
<li><code>wrapper class</code> 를 <code>parameter</code> 로 받는 메소드에서 해당 wrapper class와대응되는 primitive type의 값을 넘겨줄 때</li>
<li><code>wrapper class</code> 로 변환할 수 있을 때</li>
</ul>
</li>
<li><p>unboxing</p>
<p>  <code>autoboxing</code> 과는 반대로, <code>wrapper class</code> 의 인스턴스를 대응되는 <code>primitive type</code> 의 변수로 변환하는 것을 <code>unboxing</code> 이라고 말한다. unboxing은 다음 조건에서 수행된다.</p>
<ul>
<li><code>primitive type</code> 을 <code>parameter</code> 로 받는 메소드에서 해당 parameter type과 대응되는 <code>wrapper class</code> 의 인스턴스를 넘겨줄 때</li>
<li><code>primitive type</code> 변 로 변환할 수 있을 때</li>
</ul>
</li>
</ul>
<p><br><br></p>
<h2 id="제네릭-주요-개념-바운디드-타입-와일드-카드">제네릭 주요 개념 (바운디드 타입, 와일드 카드)</h2>
<h3 id="바운디드-타입">바운디드 타입</h3>
<p><code>type parameter</code> 에 들어갈 <code>type argument</code> 를 특정 타입으로 제한하고 싶을 수 있다. 예를 들어 <code>Number</code> 클래스의 인스턴스이거나, 서브클래스이도록 제한을 두고 싶을 수 있다. 바운디드 타입이 이러한 제한을 가능케 한다.</p>
<p><img src="https://velog.velcdn.com/images/jmsuper_97/post/0fcddbc5-ff4a-4562-917c-f86fcc9d601a/image.png" alt=""></p>
<br>

<p>위와 같이 바운디드 타입 파라미터를 정의한다. <code>U</code> 는 upper bound인 <code>Number</code> 의 인스턴스 or 서브클래스 이어야 한다.</p>
<pre><code class="language-java">public class BoundedBox&lt;T&gt; {
    private T t;
    public void set(T t){
        this.t = t;
    }
    public &lt;U extends Number&gt; void inspect(U u){
        System.out.println(&quot;T: &quot; + t.getClass().getName());
        System.out.println(&quot;U: &quot; + u.getClass().getName());
    }
    public static void main(String[] args) {
        BoundedBox&lt;Integer&gt; boundedBox = new BoundedBox&lt;&gt;();
        boundedBox.set(10);
        boundedBox.inspect(&quot;hello!&quot;); // 여기서 컴파일 에러 발생
    }
}

//출력문(에러)
//java: method inspect in class com.jm.test.BoundedBox&lt;T&gt; cannot be applied to given types;
//  required: U
//  found: java.lang.String
//  reason: inferred type does not conform to upper bound(s)
//    inferred: java.lang.String
//    upper bound(s): java.lang.Number</code></pre>
<p><code>inspect</code> 제네릭 메소드의 <code>type parameter</code> 는 <code>Number</code> 클래스를 upper bound로 삼고 있다. 그런데 <code>inspect</code> 를 호출하는 곳에서 <code>type argument</code> 로 <code>String</code> 타입의 값을 넘겨주었다. <code>String</code> 은 <code>Number</code> 클래스의 인스턴스 or 서브클래스가 아니기 때문에 type 할당이 불가능하다.</p>
<br>


<ul>
<li><p>멀티 바운드
  위에서는 하나의 바운드만 설정하였지만, <code>type parameter</code> 는 다중 바운드를 가지고 있을 수 있다.</p>
<pre><code class="language-java">  &lt;T extends B1 &amp; B2 &amp; B3&gt;</code></pre>
<p>  그런데 만약 멀티 바운드 중에 클래스가 존재한다면, 해당 클래스는 반드시 가장 맨 앞에 명시 되어야 한다.</p>
<pre><code class="language-java">  Class A { /* ... */ }
  interface B { /* ... */ }
  interface C { /* ... */ }

  class D &lt;T extends A &amp; B &amp; C&gt;{}

  class D &lt;T extends B &amp; A &amp; C&gt;{} // compile-time error</code></pre>
</li>
<li><p>제네릭 메소드와 바운디드 타입</p>
<pre><code class="language-java">  public static &lt;T&gt; int countGreaterThan(T[] anArray, T elem){
          int count = 0;
          for(T e : anArray)
              if(e &gt; elem) // compiler error
                  ++count;
          return count;
  }</code></pre>
<p>  위 함수는 정상적으로 동작할 것 같지만, 비교(<code>&gt;</code>)부분에서 컴파일 에러가 발생한다. 비교 연산자는 오직 <code>primitive type</code> 에 대해서만 수행 가능하다. <code>T</code> 는 <code>primitive type</code> 이 아닐 수 있기 때문에 에러가 발생한다.</p>
<p>  이 문제를 해결하기 위해 <code>type parameter</code> 가 <code>Compare&lt;T&gt;</code> 인터페이스를 upper bound로 삼도록 해야 한다.</p>
<pre><code class="language-java">  public interface Comparable&lt;T&gt;{
          public int compareTo(T o);
  }

  public static &lt;T extends Comparable&lt;T&gt;&gt; int countGreaterThan(T[] anArray, T elem){
          int count = 0;
          for(T e : anArray)
              if(e &gt; elem) // compiler error
                  ++count;
          return count;
  }</code></pre>
</li>
</ul>
<br>

<h3 id="제네릭-상속-그리고-서브타입">제네릭, 상속, 그리고 서브타입</h3>
<p><code>Integer</code> 는 <code>Number</code> 의 서브 클래스이다. 즉, <code>Integer</code> 는 <code>Number</code> 를 상속한다. 따라서, <code>Number type</code> 의 변수에 <code>Integer type</code> 변수가 대입될 수 있다.</p>
<p>그렇다면, <code>Box&lt;Number&gt;</code> 의 변수에 <code>Box&lt;Integer&gt;</code> 변수가 대입될 수 있을까?</p>
<pre><code class="language-java">public class GenericMethodBounded {
    public static void boxTest(Box&lt;Number&gt; n){}

    public static void main(String[] args) {
        Box&lt;Integer&gt; integerBox = new Box&lt;&gt;();
        boxTest(integerBox); // compile-time error
    }
}

class Box&lt;T&gt;{}</code></pre>
<p>결론은 “대입될 수 없다”이다. <code>Integer</code> 가 <code>Number</code> 의 서브타입이라도, <code>Box&lt;Integer&gt;</code> 는 <code>Box&lt;Number&gt;</code> 의 서브타입이 아니다.</p>
<br>

<h3 id="제네릭-타입-상속-방법">제네릭 타입 상속 방법</h3>
<pre><code class="language-java">class Box&lt;T&gt;{}

class ChildBox&lt;T&gt; extends Box{} // 1
class ChildBox&lt;T&gt; extends Box&lt;T&gt;{} // 2
class ChildBox&lt;E&gt; extends Box&lt;T&gt;{} // 3 error
class ChildBox&lt;T&gt; extends Box&lt;E&gt;{} // 4 error
class ChildBox&lt;E&gt; extends Box&lt;E&gt;{}
class ChildBox&lt;T,E&gt; extends Box&lt;T&gt;{} // 5</code></pre>
<ol>
<li><p>ChildBox는 Box를 상속 받지만, Box의 <code>type parameter</code> 인 <code>T</code> 를 상속 받지 않는다. <code>ChildBox&lt;T&gt;</code> 에서 <code>T</code> 는 ChildBox의 독립적인 <code>type parameter</code> 이다. 단, ChildBox의 생성자에서 Box의 생성자를 반드시 호출해야 한다. 그래야만, <code>Box&lt;T&gt;</code> 의 <code>type argument</code> 를 전달할 수 있기 때문이다.</p>
<pre><code class="language-java"> class ChildBox&lt;T&gt; extends Box{
         public ChildBox(T t2) {
           super(123); // 부모클래스의 생성자 호출
             this.t2 = t2;
         }
 }</code></pre>
</li>
<li><p>ChildBox는 Box를 상속 받으며, Box의 <code>type parameter</code> 인 <code>T</code> 도 상속 받는다. 따라서, <code>ChildBox&lt;T&gt;</code> 의 <code>T</code> 는 <code>Box&lt;T&gt;</code> 의 <code>T</code> 와 일치한다. 1과 마찬가지로 부모 클래스의 생성자를 호출하는 것은 동일하다.</p>
</li>
<li><p><code>Box&lt;T&gt;</code> 에 <code>T</code> 가 어떤 타입인지 알 수 없다. 자식 클래스인 <code>ChildBox</code> 에서 부모 클래스의 <code>type parameter</code> 를 정의해줘야 한다.</p>
</li>
<li><p>3과 동일한 이유로 에러가 발생한다.</p>
</li>
<li><p>정상적으로 동작한다. 자식 클래스에서 부모 클래스의 <code>type parameter</code> 의 <code>name</code> 을 따라갈 필요는 없다. 부모 클래스에게 <code>type argument</code> 만 제시해주면 된다.</p>
</li>
<li><p>부모 클래스의 <code>type parameter</code> 와 자식 클래스의 고유한 <code>type parameter</code> 를 함께 사용하려면, <code>ChildBox&lt;T, E&gt;</code> 와 같이 전부 명시해줘야 한다.</p>
</li>
</ol>
<br>

<h3 id="type-inference">Type Inference</h3>
<p>타입 추론은 자바 컴파일러가 <code>type argument</code> 를 <code>context</code>를 보고 직접 결정하는 것을 말한다. </p>
<pre><code class="language-java">public class BoxDemo {
    public static &lt;U&gt; void addBox(U u, java.util.List&lt;Box&lt;U&gt;&gt; boxes){
        Box&lt;U&gt; box = new Box&lt;&gt;();
        box.set(u);
        boxes.add(box);
    }

    public static &lt;U&gt; void outputBoxes(java.util.List&lt;Box&lt;U&gt;&gt; boxes){
        int counter = 0;
        for(Box&lt;U&gt; box : boxes){
            U boxContents = box.get();
            System.out.println(&quot;Box #&quot; + counter + &quot; contains [&quot; +
                    boxContents.toString() + &quot;]&quot;);
            counter++;
        }
    }

    public static void main(String[] args) {
        java.util.ArrayList&lt;Box&lt;Integer&gt;&gt; listOfIntegerBoxes =
                new java.util.ArrayList&lt;&gt;();
        BoxDemo.&lt;Integer&gt;addBox(Integer.valueOf(10), listOfIntegerBoxes);
        BoxDemo.addBox(Integer.valueOf(20), listOfIntegerBoxes);
        BoxDemo.addBox(Integer.valueOf(30), listOfIntegerBoxes);
        BoxDemo.outputBoxes(listOfIntegerBoxes);
    }

    static class Box&lt;T&gt;{
        private T t;
        public void set(T t){
            this.t = t;
        }
        public T get(){
            return t;
        }
    }
}

// 출력
// Box #0 contains [10]
// Box #1 contains [20]
// Box #2 contains [30]</code></pre>
<p>위 코드에서 제네릭 메소드인 <code>addBox</code> 와 <code>outputBoxes</code> 를 집중적으로 보겠다.</p>
<pre><code class="language-java">BoxDemo.&lt;Integer&gt;addBox(Integer.valueOf(10), listOfIntegerBoxes);
BoxDemo.addBox(Integer.valueOf(20), listOfIntegerBoxes);
BoxDemo.addBox(Integer.valueOf(30), listOfIntegerBoxes);
BoxDemo.outputBoxes(listOfIntegerBoxes);</code></pre>
<p>두 메소드의 호출문이다. 이상한 점은 첫번째 줄에서는 <code>&lt;Integer&gt;</code> 를 명시해주었는데, 두번째 줄부터는 <code>type argument</code> 를 지정하지 않았는데도 정상적으로 동작한다는 점이다. 이게 가능한 이유는 자바 컴파일러가 <code>type inference</code> 를 수행하였기 때문이다. 입력받은 값을 보고 <code>type argument</code> 를 유추하는 것이다.</p>
<br>

<h3 id="와일드-카드">와일드 카드</h3>
<p>제네릭 코드에서 와일드 카드라고 불리는 <code>?</code> 는 알 수 없는 타입을 명시한다. 와일드 카드는 <code>type argument</code> 로는 사용될 수 없다.</p>
<pre><code class="language-java">public &lt;? extends Number&gt; void method(){} // compile-error</code></pre>
<p>위와 같은 형태로 와일드 카드를 사용할 수 없다. 와일드 카드는 제네릭 타입 혹은 제네릭 메소드에서 <code>type argument</code> 를 대신할 수 없다.</p>
<p>그렇다면 와일드 카드는 언제 사용되며 왜 사용하는 것일까? 사실 Lower Bounded를 제외하고, Object를 사용하여 와일드 카드로 가능한 모든 것들을 구현할 수 있다. 하지만, 이는 제네릭을 사용하는 이유와 어긋나는 행동이다. 또한, 와일드 카드를 사용하면 다음과 같은 코드를 간단하게 만들 수 있다.</p>
<p>어떤 타입이든 들어갈 수 있는 List를 List에 넣는다고 가정하자. 와일드 카드로 나타내면 다음과 같다.</p>
<pre><code class="language-java">public static void main(String[] args) {
        List&lt;List&lt;?&gt;&gt; listOfAnyList = new ArrayList&lt;&gt;();
        listOfAnyList.add(new ArrayList&lt;String&gt;());
        listOfAnyList.add(new ArrayList&lt;Double&gt;());
}</code></pre>
<p>와일드 카드를 사용하지 않는 코드는 다음과 같다.</p>
<pre><code class="language-java">class ListOfAnyClassWithGeneric{
    List&lt;Object&gt; list = new ArrayList&lt;&gt;();
    public &lt;T extends List&gt; void addList(T t){
        list.add(t);
    }

        public static void main(String[] args) {
        ListOfAnyClassWithGeneric listOfAnyList2 = new ListOfAnyClassWithGeneric();
        listOfAnyList2.addList(new ArrayList&lt;String&gt;());
        listOfAnyList2.addList(new ArrayList&lt;Double&gt;());
    }
}</code></pre>
<p>제네릭 함수가 아닌 곳에서 제네릭 키워드를 사용하려면, 해당 클래스에서 제네릭 메소드를 정의해야 한다. 또한, <code>Object</code> (raw type)을 사용하는 것은 지양해야 할 방법이다. 이와 같이, 제네릭 키워드로 구현할 수 있지만, 와일드 카드로 손쉽게 구현할 수 있는 경우가 있기 때문에 와일드 카드를 사용한다고 볼 수 있다.</p>
<br>

<ol>
<li><strong>Upper Bounded Wildcard</strong>
<code>List&lt;? extends Number&gt;</code> 라고 와일드 카드를 사용할 수 있다. <code>List</code> 의 <code>type argument</code> 로 들어올 수 있는 것은 <code>Number</code> 의 인스턴스 이거나, 하위 클래스만 가능하다는 것을 의미한다. 따라서, <code>List&lt;Integer&gt;</code> or <code>List&lt;Number&gt;</code> 가 가능하다.
만약, <code>List&lt;Number&gt;</code> 를 사용한다면, <code>List&lt;Integer&gt;</code> 는 타입 캐스팅이 불가능할 것이다. 왜냐하면, <code>Integer</code> 가 <code>Number</code> 의 하위 클래스라는 것이, <code>List&lt;Integer&gt;</code> 가 <code>List&lt;Number&gt;</code> 의 하위 클래스라는 것을 보장하지 않기 때문이다.</li>
</ol>
<br>

<ol start="2">
<li><strong>Lower Bounded Wildcard</strong>
Lower Bounded는 Upper와는 반대의 개념을 갖는다. <code>List&lt;? super Number&gt;</code> 라고 명시할 경우, <code>?</code> 에 들어갈 수 있는 것은 <code>Number</code> 의 인스턴스이거나, 부모 클래스(ex. <code>Object</code>)일 경우만 가능하다. Lower Bounded는 와일드 카드에서만 가능한 특징이다.</li>
</ol>
<br>

<ol start="3">
<li><strong>Unbounded Wildcard</strong>
Unbounded는 모든 클래스가 가능하다는 것을 의미한다. <code>List&lt;?&gt;</code> 와 같이 사용한다. Unbounded는 <code>Object</code> 클래스의 메소드 혹은 제네릭 클래스에서 <code>type parameter</code> 와 상관없는 메소드를 정의할 때 사용한다.</li>
</ol>
<p><br><br></p>
<h2 id="제네릭-메소드-만들기">제네릭 메소드 만들기</h2>
<p>제네릭 메소드는 <code>type parameter</code> 를 가지고 있는 메소드를 말한다. <code>generic type</code> 과 유사해 보이지만, <code>type parameter</code> 의 스코프가 메소드에 한정된다는 점에서 차이가 있다. 즉, 제네릭 메소드의 <code>type parameter</code> 는 독립적인 파라미터이다.</p>
<p><img src="https://velog.velcdn.com/images/jmsuper_97/post/01aa1375-37f9-4b28-bf3e-7f0f19339f63/image.png" alt=""></p>
<blockquote>
<p><code>generic type</code> 은 <code>generic class</code> 와 <code>generic interface</code> 를 의미한다.</p>
</blockquote>
<p>제네릭 메소드에 대해 알기 위해 제네릭 타입과 비교하겠다.</p>
<ul>
<li><p>제네릭 타입에서 제네릭 사용</p>
<pre><code class="language-java">  class Study&lt;T&gt;{
      static T t;
  }</code></pre>
<p>  static 변수는 type parameter일 수 없다. 왜냐하면 static 변수는 클래스의 인스턴스가 생성되기 이전에 먼저 메모리에 적재되는데, <code>T</code> 가 어떤 type인지 알 수 없기 때문이다.</p>
<pre><code class="language-java">  class Study&lt;T&gt;{
      static T method(T t){
          return t;
      }
  }</code></pre>
<p>  static 메소드의 경우도 static 변수와 동일한 이유로 사용이 불가능 하다.</p>
</li>
</ul>
<br>

<ul>
<li><p>제네릭 메소드를 사용하면 type parameter를 갖는 static 함수를 정의할 수 있다.</p>
<pre><code class="language-java">  class Study&lt;T&gt;{
      static &lt;E&gt; E method(E e){
          return e;
      }
  }</code></pre>
<p>  이때 주의할 점은 제네릭 메소드의 type parameter는 제네릭 타입의 type parameter와 별개라는 것이다. 즉, 위 예제에서 <code>T</code> 와 <code>E</code> 는 서로 다른 type parameter이다. 제네릭 메소드는 자신만의 고유한 type parameter를 갖는다. 제네릭 타입에 종속적이지 않다는 특징 덕분에, static 하게 동작할 수 있다.</p>
</li>
</ul>
<br>

<ul>
<li><p>제네릭 메소드는 일반 클래스에서도 정의될 수 있다.</p>
<pre><code class="language-java">  class Study{
      static &lt;E&gt; E method(E e){
          return e;
      }
  }</code></pre>
<p>  제네릭 메소드에서는 고유한 type parameter를 갖기 때문에, 제네릭 메소드가 정의된 클래스의 Type과는 관계없이 어느 곳에서나 정의될 수 있다.</p>
</li>
</ul>
<br>

<ul>
<li><p>제네릭 메소드를 호출하는 방법</p>
<pre><code class="language-java">  public class Study {
      public &lt;T&gt; void method(T t){
          System.out.println(t);
      }

      public static void main(String[] args) {
          Study study = new Study();
          study.method(&quot;Generic method call!&quot;);
      }
  }</code></pre>
<p><br><br>    </p>
</li>
</ul>
<h2 id="erasure">Erasure</h2>
<p>제네릭은 컴파일 타임에 타이트한 타입 체킹을 수행하기 위해 등장하였다. 제네릭을 구현하기 위해 자바 컴파일러는 <code>type erasure</code> 를 수행한다. <code>type erasure</code> 는 다음과 같다.</p>
<ul>
<li>제네릭에 있는 <code>type parameter</code> 를 <code>Object</code> 또는 <code>upper bounded class</code> 로 대체한다. 따라서 바이트코드에는 제네릭이 아닌 일반적인 클래스 코드들이 저장된다.</li>
<li>type safety가 보장되어야 하는 곳에 <code>type cast</code> 를 추가한다.</li>
<li>제네릭 타입의 상속이 있는 곳에 <code>bridge method</code> 를 추가한다.</li>
</ul>
<h3 id="erasure-of-generic-types">Erasure of Generic Types</h3>
<pre><code class="language-java">public class Node&lt;T&gt;{
    private T data;
    private Node&lt;T&gt; next;

    public Node(T data, Node&lt;T&gt; next){
        this.data = data;
        this.next = next;
    }

    public T getData(){return data;}
}</code></pre>
<p>위와 같은 제네릭 타입에서 <code>type parameter</code> 들은 <code>Object</code> 또는 <code>upper bounded class</code> 로 대체된다. 이렇게 대체함으로서, 실제 객체가 생성될 때 <code>type argument</code> 로 들어올 수 있는 모든 타입들을 수용할 수 있게 한다. </p>
<pre><code class="language-java">public class Node{
    private Object data;
    private Node next;

    public Node(Object data, Node next){
        this.data = data;
        this.next = next;
    }

    public Object getData(){return data;}
}</code></pre>
<h3 id="bridge-methods">Bridge Methods</h3>
<pre><code class="language-java">public class Node&lt;T&gt;{
    public T data;
    public Node(T data){this.data = data;}
    public void setData(T data){this.data = data;}

    public static void main(String[] args) {
        MyNode mn = new MyNode(5);
        Node n = mn;
        n.setData(&quot;Hello&quot;);
        Integer x = mn.data;
    }
}

class MyNode extends Node&lt;Integer&gt;{
    public MyNode(Integer data) {
        super(data);
    }
    public void setData(Integer data){super.setData(data);}
}

//출력문
//Exception in thread &quot;main&quot; java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
//    at com.jm.test.MyNode.setData(Node.java:16)
//    at com.jm.test.Node.main(Node.java:11)</code></pre>
<p>위 코드는 런타임 에러(<code>ClassCastException</code>)를 발생시킨다. 제네릭은 컴파일 타임에 타입 체킹을 수행하는데 어째서 런타임 에러가 발생하는 것일까?</p>
<pre><code class="language-java">public class Node{
    public Object data;
    public Node(Object data){this.data = data;}
    public void setData(Object data){this.data = data;}

    public static void main(String[] args) {
        MyNode mn = new MyNode(5);
        Node n = mn;
        n.setData(&quot;Hello&quot;);
        Integer x = mn.data;
    }
}

class MyNode extends Node&lt;Integer&gt;{
    public MyNode(Integer data) {
        super(data);
    }
    public void setData(Integer data){super.setData(data);}
}</code></pre>
<p><code>type erasure</code> 가 수행된 이후의 코드이다. <code>Node</code> 의 <code>setData(Object data)</code> 와 <code>MyNode</code> 의 <code>setData(Integer data)</code> 는 서로 파라미터의 타입이 다르기 때문에 오버라이딩이 이뤄지지 않는다. 이 문제를 해결하기 위해 자바 컴파일러는 <code>Bridge method</code> 를 추가한다. <code>Bridge method</code> 는 다음과 같다.</p>
<pre><code class="language-java">public void setData(Object data){
        setData((Integer) data);
}</code></pre>
<p>이를 통해, 오버라이딩이 진행된다. <code>main()</code> 에서 <code>n.setData(&quot;Hello&quot;)</code> 는 <code>MyNode.setData(Object data)</code> 를 호출하고, 해당 메소드에서는 <code>setData((Integer) data)</code> 를 호출한다. 그런데, 이때 넘겨받은 값은 <code>String</code> 이기 때문에 <code>Integer</code> 로 타입 캐스팅 되지 않는다. 따라서, <code>ClassCastException</code> 이 발생하게 된다.</p>
<blockquote>
<p>주의할점! 자바는 Dynamic method dispatch를 지원한다. 실행될 메소드를 결정지을 때 컴파일 타임이 아닌, 런타임에 수행된다. 즉, 부모 클래스 변수에서 자식 클래스 객체를 가리키고 있을 때, 오버라이딩된 메소드를 호출하면 부모 클래스의 메소드가 아닌 자식 클래스의 메소드가 호출된다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] I/O]]></title>
            <link>https://velog.io/@jmsuper_97/Java-IO</link>
            <guid>https://velog.io/@jmsuper_97/Java-IO</guid>
            <pubDate>Wed, 21 Sep 2022 10:56:22 GMT</pubDate>
            <description><![CDATA[<h3 id="학습할-것-필수"><strong>학습할 것 (필수)</strong></h3>
<ul>
<li>스트림 (Stream) / 버퍼 (Buffer) / 채널 (Channel) 기반의 I/O</li>
<li>InputStream과 OutputStream</li>
<li>Byte와 Character 스트림</li>
<li>표준 스트림 (System.in, System.out, System.err)</li>
<li>파일 읽고 쓰기</li>
</ul>
<br>

<blockquote>
<p>참고자료</p>
</blockquote>
<ul>
<li><a href="https://www.geeksforgeeks.org/java-io-tutorial/">https://www.geeksforgeeks.org/java-io-tutorial/</a></li>
<li><a href="https://stackoverflow.com/questions/39550670/java-difference-between-streams-and-i-o-stream-explained">https://stackoverflow.com/questions/39550670/java-difference-between-streams-and-i-o-stream-explained</a></li>
<li><a href="https://byul91oh.tistory.com/267">https://byul91oh.tistory.com/267</a></li>
<li><a href="https://homoefficio.github.io/2016/08/06/Java-NIO%EB%8A%94-%EC%83%9D%EA%B0%81%EB%A7%8C%ED%81%BC-non-blocking-%ED%95%98%EC%A7%80-%EC%95%8A%EB%8B%A4/">https://homoefficio.github.io/2016/08/06/Java-NIO는-생각만큼-non-blocking-하지-않다/</a></li>
<li>책 : 자바의 신(저자 : 이상민)</li>
<li>책 : 자바의 정석(저자 : 남궁 성)</li>
</ul>
<p><br><br></p>
<h2 id="스트림-stream--버퍼-buffer--채널-channel-기반의-io">스트림 (Stream) / 버퍼 (Buffer) / 채널 (Channel) 기반의 I/O</h2>
<h3 id="스트림">스트림</h3>
<p> 자바에서는 I/O를 구현하기 위해 <code>stream</code> 이라는 개념을 도입하였다. <code>stream</code> 은 데이터의 연속이라고 묘사된다. 왜냐하면 계속해서 데이터가 흐르는 것과 유사하기 때문이다. 배열의 개념과는 다르게, <code>stream</code> 은 <code>indexing</code> 개념을 지원하지 않는다. <code>stream</code> 은 오직 데이터의 근원과 목적지에만 집중한다. 이러한 특징 때문에, <code>stream</code> 에서는 데이터 안에서 앞 혹은 뒤로 움직일 수 없다. 그리고 단방향이며, FIFO로 동작한다.</p>
<p> 또한, <code>stream</code> 은 blocking으로 동작한다는 특징이 있다. 아래 코드는 사용자 입력을 출력하는 코드이다. 사용자의 입력이 들어올 때까지 <code>read()</code> 이후의 코드는 실행되지 않는다.</p>
<pre><code class="language-java">public class BlockingIOTest {
    public static void main(String[] args) throws IOException {
        InputStreamReader inputStreamReader =
                new InputStreamReader(System.in);
        System.out.println(inputStreamReader.read());

        System.out.println(&quot;finished!&quot;);
    }
}
// 입력
// a
// 출력
// 97
// finished!</code></pre>
<p><img src="https://velog.velcdn.com/images/jmsuper_97/post/8b73bf4d-ac1e-4cbb-acb6-ac9b4eed83bf/image.png" alt=""></p>
<br>
위 그림은 stream의 동작 방식을 묘사하고 있다. stream의 주요한 요소는 `source` , `Element` , `Destination` 이다. `Source` 와 `Destination` 은 파일 or 네트워크 연결 or 파이프 or 메모리 버퍼 등이 될 수 있다. `Element` 는 그저 데이터의 조각이며 `stream` 은 그러한 데이터들의 모음을 가지고 있는 추상적인 개념이다. 자바의 기본 I/O 들은 대부분 `stream` 으로 구현되어있다.

<blockquote>
<p>자바8부터 도입된 stream API로 인해 stream I/O의 개념에 대한 혼동이 있을 수 있다. I/O stream은 자바에서 I/O를 다루기 위해 도입한 추상적인 개념이다. stream API는 I/O stream과 상관 없으며, 데이터의 집합을 다루는 함수적인 접근법을 제공하는 API이다.</p>
</blockquote>
<p><br><br></p>
<h3 id="버퍼">버퍼</h3>
<p>프로그래밍에서 버퍼는 CPU와 보조기억장치 사이의 임시 저장공간을 의미한다. CPU 매우 빠른데 비해, 보조기억장치에서 CPU에게 데이터를 전달하는 속도를 느리다. 따라서, CPU가 보조기억장치에서 전달하는 데이터를 기다리는 것은 손해이다.</p>
<p>이러한 문제를 해결하기 위해 버퍼라는 임시 저장공간을 두어, 보조기억장치에서 버퍼에 데이터를 적재하는 것을 완료하면, 그때 CPU가 버퍼에서 데이터를 읽어들이도록 한다. CPU는 보조기억장치를 기다리지 않아도 되기 때문에 효율적으로 일을 처리할 수 있다.</p>
<p>버퍼 I/O를 처리하기 위해 <code>unbuffered</code> stream을 버퍼 스트림으로 감싼다.</p>
<p>버퍼를 사용하는 I/O를 위해 BufferedInputStream 클래스를 사용하겠다. 해당 클래스는 내부적으로 버퍼를 사용하여 퍼포먼스에서 우위를 갖는다. stream에서 데이터를 skip 하거나 읽거오며, 내부 버퍼는 자동적으로 input stream으로 부터 데이터를 채운다. 많은 바이트를 한번에 채운다. BufferedInputStream이 생성되면 내부 버퍼 배열도 생성된다.</p>
<br>

<pre><code class="language-java">public class BufferInputStreamTest {
    public static void main(String[] args) throws IOException {
        FileInputStream fileInputStream = new FileInputStream(&quot;text.txt&quot;);
        BufferedInputStream br = new BufferedInputStream(fileInputStream);
        int c = 1;
        while((c = br.read()) &gt; 0) {
            System.out.print((char) c);
        }
    }
}</code></pre>
<p>BufferedInputStream의 인스턴스가 파일을 읽어들이는 것 처럼 보이지만, 실제로는 BufferedInputStream은 buffer만 제공할 뿐이며, read()를 수행하는 것은 FileInputStream의 인스턴스이다. BufferedInputStream와 같이 보조하는 역할을 하는 스트림을 보조 스트림이라고 한다.</p>
<p><br><br></p>
<h3 id="채널">채널</h3>
<p>채널은 new I/O인 NIO에서 지원하는 I/O 클래스이다. 단방향으로 동작하는 <code>stream</code> 과는 다르게, <code>channel</code> 은 양방향이다. 그리고, <code>stream</code> 은 단독으로 source와 연결되어 I/O가 가능하지만, NIO는 항상 버퍼로 데이터를 읽어들이거나 쓴다. NIO는 비동기와 Non-blocking을 지원한다는 장점이 있다.</p>
<pre><code class="language-java">public class NioSample {
    public static void main(String[] args) {
        NioSample nioSample = new NioSample();
        nioSample.basicWriteAndRead();
    }

    public void basicWriteAndRead(){
        String filename = &quot;text.txt&quot;;
        try {
            writeFile(filename,&quot;My first NIO sample&quot;);
            readFile(filename);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public void writeFile(String filename, String data) throws IOException {
        // FileChannel 객체를 만들려면, FileOutputStream 클래스에 선언된 getChannel()을 호출한다.
        FileChannel channel = new FileOutputStream(filename).getChannel();
        byte[] byteData = data.getBytes();
        // ByteBuffer 클래스의 static 메소드인 wrap()을 호출하면 ByteBuffer 객체를 반환한다.
        // ByteBuffer는 abstract 클래스이기 때문에, 해당 클래스를 구현한 구현체의 인스턴스를
        // 리턴하는 것이다. 또한, Buffer객체가 필요한 이유는 Channel 클래스에서
        // Buffer객체를 이용해 대상과 데이터를 주고받기 때문이다.
        ByteBuffer buffer = ByteBuffer.wrap(byteData);
        // write()메소드에 buffer 객체를 넘겨주면 파일에 데이터를 쓰이게 된다.
        channel.write(buffer);
        channel.close();
    }

    public void readFile(String fileName) throws Exception{
        // writeFile() 메소드에서와 동일하게 File***Stream 객체로 부터 Channel 객체를 가져온다.
        FileChannel channel = new FileInputStream(fileName).getChannel();
        // Buffer 인스턴스를 생성하는 다른 방법이다.
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        // read() 메소드에 buffer 객체를 넘겨주어 읽어드리기 위한 준비를 한다.
        channel.read(buffer);
        // buffer 객체는 CD와 같이 현재 위치를 가지고 있다.
        // flip()은 그러한 위치를 처음으로 옮기고, 버퍼가 읽거나 쓸 수 없는 첫 번째 위치를
        // 나타내는 limit을 맨 끝으로 이동시켜준다.
        buffer.flip();
        while(buffer.hasRemaining()){
            System.out.print((char)buffer.get());
        }
        channel.close();
    }
}</code></pre>
<br>

<blockquote>
<p><code>channel</code> 이 양방향으로 동작한다는 말 때문에, 하나의 버퍼를 통해 데이터가 오고 갈 수 있다는 말인지 아니면, <code>Channel</code> 의 인스턴스가 input일 수도 있고 output일 수도 있다는 말인지 혼동이 되었다.</p>
<p><img src="https://velog.velcdn.com/images/jmsuper_97/post/4b59d11d-7a12-4327-86fa-8c9a7ec1eb9d/image.png" alt=""></p>
<p>확인해보니, <code>Channel</code> 클래스는 Input과 Output의 역할을 모두 할 수 있다는 의미였다.</p>
</blockquote>
<p><br><br></p>
<h2 id="inputstream과-outputstream">InputStream과 OutputStream</h2>
<p><code>InputStream과 OutputStream은 자바 스트림들의 부모 클래스이다.</code></p>
<p>자바 IO는 <code>InputStream</code>과 <code>OutputStream</code> 이라는 추상 클래스를 통해 제공된다.</p>
<pre><code class="language-java">public abstract class InputStream implements Closeable {}
public abstract class OutputStream implements Closeable, Flushable {}

public interface Closeable extends AutoCloseable {
        public void close() throws IOException;
}

public interface Flushable {
        void flush() throws IOException;
}</code></pre>
<br>

<p>두 클래스 모두 <code>Closeable</code> 인터페이스를 구현하고 있다. <code>Closeable</code> 인터페이스는 <code>close()</code> 메소드를 가지고 있는데, 해당 메소드를 통해 스트림으로 연결되 있던 대상을 닫아주는 것이다. 닫아주지 않으면 해당 대상을 다른 클래스에서 작업할 수 없기 때문이다.</p>
<p><code>OutputStream</code> 클래스는 추가적으로 <code>Flushable</code> 인터페이스를 구현하고 있다. <code>flush()</code> 는 버퍼에 있는 데이터를 <code>stream</code> 으로 연결된 목적지에 전부 쓰도록 한다. 다시 말해, “현재 버퍼에 있는 내용을 지금 저장해!” 라고 명령을 내리는 것이다.</p>
<br>

<h3 id="inputstream">InputStream</h3>
<ul>
<li>메소드</li>
</ul>
<pre><code>| 리턴 타입 | 메소드 이름 및 매개 변수 | 설명 |
| --- | --- | --- |
| int | available() | 스트림에서 중단없이 읽을 수 있는 바이트의 개수를 리턴한다. |
| void | mark(int readlimit) | 스트림의 현재 위치를 표시(mark)해 둔다. 여기서 매개 변수로 넘긴 int 값은 표시해둔 자리의 최대 유효 길이이다. 이 값을 넘어가면, 표시해 둔 자리는 더 이상 의미가 없어진다. |
| void | reset() |  위치를 mark() 메소드가 호출되었던 위치로 되돌린다. |
| boolean | markSupported() | mark()나 reset() 메소드가 수행 가능한지를 확인한다. |
| abstract int | read() | 스트림에서 다음 바이트를 읽는다. 이 클래스에 유일한 추상 메소드이다. |
| int | read(byte[] b) | 매개 변수로 넘어온 바이트 배열에 데이터를 담는다. 리턴 값은 데이터를 담은 개수다. |
| int | read(byte[] b, int off, int len) | 매개 변수로 넘어온 바이트 배열에 특정 위치(off)부터 지정한 길이(len) 만큼의 데이터를 담는다. 리턴 값은 데이터를 담은 개수다. |
| long | skip(long n) | 매개 변수로 넘어온 길이(n)만큼의 데이터를 건너 뛴다. |
| void | close() | 스트림에서 작업중인 대상을 해제한다. 이 메소드를 수행한 이후에는 다른 메소드를 사용하여 데이터를 처리할 수 없다. |</code></pre><ul>
<li><p>자식 클래스</p>
<p>  <img src="https://velog.velcdn.com/images/jmsuper_97/post/1c0c9823-9214-4171-baa2-cb21c3830670/image.png" alt=""></p>
</li>
</ul>
<pre><code>위 클래스들 중 많이 사용되는 클래스는 다음과 같다.

| 클래스 | 설명 |
| --- | --- |
| FileInputStream | 파일을 읽는 데 사용한다. 주로 우리가 쉽게 읽을 수 있는 텍스트 파일을 읽기 위한 용도라기보다, 이미지와 같이 바이트 코드로 된 데이터를 읽을 때 사용한다. |
| FilterInputStream | 이 클래스는 다른 입력 스트림을 포괄하며, 단순히 InputStream 클래스가 Override되어 있다. |
| ObjectInputStream | ObjectOutputStream으로 저장한 데이터를 읽는데 사용한다. |

```java
public class FileInputStreamTest {
    public static void main(String[] args) throws IOException {
        FileInputStream fileInputStream = new FileInputStream(&quot;file.txt&quot;);
        int c = 1;
        while( (c = fileInputStream.read()) &gt; 0){
            System.out.print((char) c);
        }
        fileInputStream.close();
    }
}
// 출력
// ABCDEFGHIJ
```

```java
public class ObjectInputStreamTest {
    public static void main(String[] args) throws IOException {
        String fileName = &quot;object.txt&quot;;

        try(ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(fileName))){
            Data data = new Data(&quot;JM&quot;);
            objectOutputStream.writeObject(data);
        }catch (Exception e){
            e.printStackTrace();
        }
        try(ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(fileName))){
            Data data2 = (Data) inputStream.readObject();
            System.out.println(data2.getName());
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

class Data implements Serializable{
    private String name;
    Data(String name) {this.name = name;}
    public String getName() {return name;}
    public void setName(String name) {this.name = name;}
}
// 출력
// JM
```

`FilterInputStream` 클래스의 생성자는 protected로 선언되어 있기 때문에, 확장한 클래스에서만 객체를 생성할 수 있다. 따라서, 이 클래스를 확장한 클래스를 통해 객체를 생성한다.

```java
public class FilterInputStream extends InputStream {
        protected FilterInputStream(InputStream in) {
                this.in = in;
    }
        ...
}
```

FilterInputStream을 확장한 클래스

`BufferedInputStream` , `CheckedInputStream` , `CipherInputStream` , `DataInputStream` , `DeflaterInputStream` , `DigestInputStream` , `InflaterInputStream` , `LineNumberInputStream` , `ProgressMonitorInputStream` , `PushbackInputStream`</code></pre><p><br><br></p>
<h3 id="outputstream">OutputStream</h3>
<ul>
<li>메소드</li>
</ul>
<pre><code>| 리턴 타입 | 메소드 이름 및 매개 변수 | 설명 |
| --- | --- | --- |
| void | write(byte[] b) | 매개 변수로 받은 바이트 배열(b)를 저장한다. |
| void | write(byte[] b, int off, int len) | 매개 변수로 받은 바이트 배열(b)의 특정 위치(off)부터 지정한 길이(len)만큼 저장한다. |
| abstract void | write(int b) | 매개 변수로 받은 바이트를 저장한다. 타입은 int이지만, 실제 저장되는 것은 바이트로 저장된다. |
| void | flush() | 버퍼에 쓰려고 대기하고 있는 데이터를 강제로 쓰도록 한다. |
| void | close() | 쓰기 위해 열은 스트림을 해제한다. |

```java
public class OutputStreamExample {
    public static void main(String[] args) throws IOException {
        OutputStream output = new FileOutputStream(&quot;file.txt&quot;);
        byte b[] = {65,66,67,68,69,70};

        // illustrating write(nyte[] b) method
        // b 배열에서 요소 1개씩 write한다.
        output.write(b);
        // illustrating write(int b) method
        for(int i = 71; i &lt; 75; i++){
            output.write(i);
        }
        output.close();
    }
}
// file.txt
// ABCDEFGHIJ
```</code></pre><ul>
<li>자식 클래스</li>
</ul>
<p><img src="https://velog.velcdn.com/images/jmsuper_97/post/888a75c6-7cd6-4bb6-9ba6-0ebdccf54caa/image.png" alt=""></p>
<p><br><br></p>
<h2 id="byte와-character-스트림">Byte와 Character 스트림</h2>
<p><code>stream</code> 은 다음과 같은 타입들을 갖는다.</p>
<p><img src="https://velog.velcdn.com/images/jmsuper_97/post/2247685b-6670-4a0c-b0b1-fb3ba2f4b5b3/image.png" alt=""></p>
<p><code>Byte stream</code> 은 8-bit를 기준으로 하며, <code>Character stream</code> 은 16-bit를 기준으로 한다.</p>
<h3 id="byte-stream">Byte Stream</h3>
<p><code>***Stream</code> 이라고 명시된 클래스는 <code>Byte Stream</code> 에 해당한다. 대표적인 클래스는 <code>FileInputStream</code> 과 <code>FileOutputStream</code> 이며,  클래스들은 다음과 같다.</p>
<table>
<thead>
<tr>
<th>Stream Class</th>
<th>Description</th>
</tr>
</thead>
<tbody><tr>
<td>InputStream</td>
<td>스트림 Input을 정의하는 추상 클래스이다.</td>
</tr>
<tr>
<td>FileInputStream</td>
<td>파일을 읽기 위해 사용된다.</td>
</tr>
<tr>
<td>DataInputStream</td>
<td>자바 표준 데이터 타입을 읽기 위한 메소드들이 정의되있다.</td>
</tr>
<tr>
<td>BufferedInputStream</td>
<td>InputStream에 Buffer를 추가하였다.</td>
</tr>
<tr>
<td>PrintStream</td>
<td>자주 사용되는 print() , println() 을 포함하고 있다.</td>
</tr>
<tr>
<td>OutputStream</td>
<td>스트림 Output을 정의하는 추상 클래스이다.</td>
</tr>
<tr>
<td>FileOutputStream</td>
<td>파일에 쓰기 위한 클래스이다.</td>
</tr>
<tr>
<td>DataOutputStream</td>
<td>DataOuputStream</td>
</tr>
<tr>
<td>BufferdOutputStream</td>
<td>OutputStream에 Buffer를 추가하였다.</td>
</tr>
</tbody></table>
<br>

<h3 id="character-stream">Character Stream</h3>
<p><code>char</code> 기반의 문자열을 처리하기 위한 클래스이다. <code>Byte Stream</code> 에서의 <code>InputStream</code> 과 <code>OutputStream</code> 처럼, <code>Reader</code> , <code>Writer</code> 클래스가 있다.</p>
<pre><code class="language-java">public abstract class Reader implements Readable, Closeable {
        protected Reader() {
        this.lock = this;
    }
        ...
}
public abstract class Writer implements Appendable, Closeable, Flushable {
        protected Writer() {
        this.lock = this;
    }
}</code></pre>
<p><code>Readable</code> 인터페이스는 <code>CharBuffer</code> 를 읽는 메소드를 선언한다. <code>Appendable</code> 인터페이스는 각종 문자열을 추가하기 위해 선언되었다. <code>Reader</code> 와 <code>Writer</code> 의 메소드는 <code>InputStream</code> , <code>OutputStream</code> 과 거의 유사하다. 추가된 사항들만 열거하겠다.</p>
<ul>
<li><p>Reader</p>
<ul>
<li><code>ready()</code> : Reader에서 작업할 대상이 읽을 준비가 되어 있는지를 확인한다.</li>
<li><code>read(CharBuffer target)</code> : 매개 변수로 넘어온 CharBuffer 클래스의 객체에 데이터를 담는다. 리턴 값은 데이터를 담은 개수다.</li>
</ul>
</li>
<li><p>Writer</p>
<ul>
<li><p><code>append(char c)</code> : 매개변수로 넘어온 char를 추가한다.</p>
</li>
<li><p><code>append(CharSequence csq)</code> : 매개변수로 넘어온 CharSequence를 추가한다.</p>
<p>CharSequence는 인터페이스이다. 이 인터페이스를 구현한 대표적인 클래스에는 String, StringBuilder, StringBuffer가 있다. 그래서, 매개 변수로 CharSequence를 넘긴다는 것은 대부분의 문자열을 다 받아서 처리한다는 말이다.</p>
</li>
</ul>
</li>
</ul>
<br>

<p>Char Stream의 대표적인 클래스는 다음과 같다.</p>
<table>
<thead>
<tr>
<th>Stream Classes</th>
<th>Description</th>
</tr>
</thead>
<tbody><tr>
<td>Reader</td>
<td>character stream input을 정의하는 추상 클래스이다.</td>
</tr>
<tr>
<td>Writer</td>
<td>character stream output을 정의하는 추상 클래스이다.</td>
</tr>
<tr>
<td>FileReader</td>
<td>파일을 읽는 input stream 이다.</td>
</tr>
<tr>
<td>FileWriter</td>
<td>파일에 쓰는 output stream 이다.</td>
</tr>
<tr>
<td>BufferedReader</td>
<td>버퍼가 추가된 input stream을 다룬다.</td>
</tr>
<tr>
<td>BufferedWriter</td>
<td>버퍼가 추가된 output stream을 다룬다.</td>
</tr>
<tr>
<td>InputStreamReader</td>
<td>byte를 char로 변환하기 위해 사용되는 input stream이다.</td>
</tr>
<tr>
<td>OutputStreamReader</td>
<td>byte를 char로 변환하기 위해 사용되는 output stream이다.</td>
</tr>
<tr>
<td>PrintWriter</td>
<td>print() , println() 을 포함하고 있다.</td>
</tr>
</tbody></table>
<p><br><br></p>
<h2 id="표준-스트림-systemin-systemout-systemerr">표준 스트림 (System.in, System.out, System.err)</h2>
<p><img src="https://velog.velcdn.com/images/jmsuper_97/post/c8b83524-49ee-4203-9083-0a8d853a3715/image.png" alt=""></p>
<ul>
<li>System.in : 사용자의 프로그램으로 부터 입력을 받기 위한 표준 Input 클래스이다. 보통 키보드가 표준 입력 스트림으로 사용된다.</li>
<li>System.out : 사용자의 프로그램으로 부터 생성된 데이터를 출력하기 위한 표준 output 클래스이다. 보통 모니터가 표준 출력 스트림으로 사용된다.</li>
<li>System.err : 사용자의 프로그램으로 부터 발생한 에러 데이터를 출력하기 위한 표준 error 클래스이다.  보통 모니터가 표준 오류 스트림으로 사용된다.</li>
</ul>
<p><br><br></p>
<h2 id="파일-읽고-쓰기">파일 읽고 쓰기</h2>
<p>파일에 char 기반의 내용을 쓰기 위해 <code>FileWriter</code> 라는 클래스를 사용하겠다. <code>FileWriter</code> 클래스의 생성자는 다음과 같다.</p>
<table>
<thead>
<tr>
<th>생성자</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>FileWriter(File file)</td>
<td>File 객체를 매개 변수로 받아 객체를 생성한다.</td>
</tr>
<tr>
<td>FileWriter(Flie file, boolean append)</td>
<td>File 객체를 매개 변수로 받아 객체를 생성한다. append 값을 통하여 해당 파일의 뒤에 붙일지(append = true), 해당 파일을 덮어 쓸지(append = false)를 정한다.</td>
</tr>
<tr>
<td>FileWriter(FileDescriptor fd)</td>
<td>FileDescriptor 객체를 매개 변수로 받아 객체를 생성한다.</td>
</tr>
<tr>
<td>FileWriter(String fileName)</td>
<td>지정한 문자열의 디렉토리와 파일 이름에 해당하는 객체를 생성한다.</td>
</tr>
<tr>
<td>FileWriter(String fileName)</td>
<td>지정한 문자열의 디렉토리와 파일 이름에 해당하는 객체를 생성한다. append 값에 따라서, 데이터를 추가할지, 덮어쓸지를 정한다.</td>
</tr>
</tbody></table>
<br>

<p>그런데, Writer에 있는 <code>write()</code> 메소드나 <code>append()</code> 메소드는 파일에 직접 접근하여 데이터를 쓰기 때문에 비효율적이다. 이를 해결하기 위해 <code>BufferWriter</code> 클래스를 활용한다.</p>
<br>

<table>
<thead>
<tr>
<th>생성자</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>BufferedWriter(Writer out)</td>
<td>Writer 객체를 매개 변수로 받아 객체를 생성한다.</td>
</tr>
<tr>
<td>BufferedWriter(Writer out, int size)</td>
<td>Writer 객체를 매개 변수로 받아 객체를 생성한다. 그리고, 두 번째 매개 변수에 있는 size를 사용하여, 버퍼의 크기를 정한다.</td>
</tr>
</tbody></table>
<br>

<p><code>BufferWriter</code> 클래스는 <code>Writer</code> 클래스의 인스턴스를 받아, 버퍼가 차게되면 데이터를 저장하도록 도와주는 역할을 수행한다. </p>
<pre><code class="language-java">public class ManageTextFile {
    public static void main(String[] args) {
        ManageTextFile manager = new ManageTextFile();
        int numberCount = 10;
        String fileName = &quot;numbers.txt&quot;;
        manager.writeFile(fileName, numberCount);
        manager.readFile(fileName);
    }

    public void writeFile(String fileName, int numberCount){
        FileWriter fileWriter = null;
        BufferedWriter bufferedWriter = null;
        try{
            fileWriter = new FileWriter(fileName);
            bufferedWriter = new BufferedWriter(fileWriter);
            for(int loop = 0; loop &lt; numberCount; loop++){
                bufferedWriter.write(Integer.toString(loop));
                bufferedWriter.write(&quot; &quot;);
            }
            System.out.println(&quot;Write success!!&quot;);
        } catch (Exception e){
            e.printStackTrace();
        }finally {
            if(bufferedWriter != null){
                try {
                    bufferedWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fileWriter != null){
                try {
                    fileWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public void readFile(String fileName){
        FileReader fileReader = null;
        BufferedReader bufferedReader = null;
        try{
            fileReader = new FileReader(fileName);
            bufferedReader = new BufferedReader(fileReader);
            String data;
            while ((data = bufferedReader.readLine()) != null){
                System.out.print(data + &quot; &quot;);
            }
            System.out.println(&quot;\nRead success!!&quot;);
        } catch (Exception e){
            e.printStackTrace();
        }finally {
            if(bufferedReader != null){
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fileReader != null){
                try {
                    fileReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
// Write success!!
// 0 1 2 3 4 5 6 7 8 9  
// Read success!!</code></pre>
]]></description>
        </item>
    </channel>
</rss>