<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>jang_brick.log</title>
        <link>https://velog.io/</link>
        <description>나는야 토마토</description>
        <lastBuildDate>Wed, 07 Feb 2024 10:07:24 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>jang_brick.log</title>
            <url>https://velog.velcdn.com/images/jang_tissue/profile/ff018656-6ffc-4638-958c-89d6cdd65373/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. jang_brick.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/jang_tissue" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[JAVA의 원시타입은 null 값을 싫어해..]]></title>
            <link>https://velog.io/@jang_tissue/JAVA%EC%9D%98-%EC%9B%90%EC%8B%9C%ED%83%80%EC%9E%85%EC%9D%80-null-%EA%B0%92%EC%9D%84-%EC%8B%AB%EC%96%B4%ED%95%B4</link>
            <guid>https://velog.io/@jang_tissue/JAVA%EC%9D%98-%EC%9B%90%EC%8B%9C%ED%83%80%EC%9E%85%EC%9D%80-null-%EA%B0%92%EC%9D%84-%EC%8B%AB%EC%96%B4%ED%95%B4</guid>
            <pubDate>Wed, 07 Feb 2024 10:07:24 GMT</pubDate>
            <description><![CDATA[<h2 id="어떻게-알았어">어떻게 알았어?</h2>
<hr>

<p>환자의 생체 데이터를 활용하여 패혈증 지수를 예측하는 서비스를 만드는 프로젝트 진행 중 Pysionet 2019 에서 제공하는 환자 데이터를 DB에 입력하였다.</p>
<p>이후 입력된 데이터를 JPA를 사용해서 가져오려고 했다.</p>
<pre><code class="language-java">@Entity
@Data
@Persistent
public class Smart_vital {

    //바이탈 넘버
    @Id
    @Column
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int num;

    // 환자번호
    @Column
    private int patientnum;

    // 패혈증 수치
    @Column
    private float sepsisscore;

    // 검사 일시 default값 지정
    @Column(columnDefinition = &quot;datetime default now()&quot;, insertable = false, updatable = false)
    private String sepdate;
    // 산소포화도
    @Column
    private float o2sat;

    // 체온
    @Column
    private float temp;
    // 수축기 혈압
    @Column
    private int sbp;
    // 이완기 혈압
    @Column
    private int dbp;
    //호흡수
    @Column
    private int resp;
    // 심장박동수
    @Column
    private int hr;
    // 평균 동맥압 평균 혈압
    @Column
    private float map;

    //이산화 탄소 농도
    @Column
    private float etco2;

    //과잉 탄산염
    @Column
    private float baseexcess;

    //탄산수소이온
    @Column
    private float hco3;

    //흡기 손소분율
    @Column
    private float fio2;

    //산성 또는 알카리성 정도를 나타내는 척도
    @Column
    private float ph;

    //이산화 탄소 분압
    @Column
    private float paco2;

    //동맥혈의 산소포화도
    @Column
    private float sao2;

    //간 기능을 평가하기위해 사용하는 효소
    @Column
    private float ast;

    //신장기능 평가
    @Column
    private float bun;

    //알카리성 인산효소
    @Column
    private float alkalinephos;

    //칼슘
    @Column
    private float calcium;

    //염소이온
    @Column
    private float chloride;

    //크레아틴
    @Column
    private float creatinine;

    //직접빌리루빈
    @Column
    private float bilirubindirect;

    //혈당
    @Column
    private float glucose;

    //라틱산
    @Column
    private float lactate;

    //마그네슘
    @Column
    private float magnesium;

    //인
    @Column
    private float phosphate;

    //칼륨
    @Column
    private float potassium;

    //총 빌리루빈
    @Column
    private float bilirubintotal;

    //트로포닌
    @Column
    private float troponini;

    //헤마토크리트
    @Column
    private float hct;

    //헤모글로빈
    @Column
    private float hgb;

    //부분혈장응고시간
    @Column
    private float ptt;

    //백혈구 수
    @Column
    private float wbc;

    //혈액응고 역할 단백질
    @Column
    private float fibrinogen;

    //혈소판 수
    @Column
    private float platelets;

}</code></pre>
<p>(아무리 봐도 너무 많은 생체 데이터 컬럼...)</p>
<p>근데 실행해보니 ㄷㄷ..</p>
<pre><code class="language-java">2024-02-07 17:20:40.260 ERROR 10904 --- [nio-8088-exec-2] o.a.c.c.C.[.[.[.[dispatcherServlet]      : Servlet.service() for servlet [dispatcherServlet] in context with path [/boot] threw exception [Request processing failed; nested exception is org.springframework.orm.jpa.JpaSystemException: Null value was assigned to a property [class com.smhrd.smart.entity.smart_vital1.alkalinephos] of primitive type setter of com.smhrd.smart.entity.smart_vital1.alkalinephos; nested exception is org.hibernate.PropertyAccessException: Null value was assigned to a property [class com.smhrd.smart.entity.smart_vital1.alkalinephos] of primitive type setter of com.smhrd.smart.entity.smart_vital1.alkalinephos] with root cause</code></pre>
<p>이런 끔찍한 오류가 발생했다..</p>
<p>바로 Googling GoGo~~</p>
<br>

<h2 id="어떤-오류">어떤 오류?</h2>
<hr>

<p><img src="https://velog.velcdn.com/images/jang_tissue/post/60c65394-ec4f-4ed6-8cb4-5d657ab82998/image.png" alt=""></p>
<p>patient_vital 테이블에 존재하는 데이터는 이렇게 Null 값이 존재한다.</p>
<p>그래서 처음에 JPA는 NULL 값을 못받는 줄 알고 <strong>@Column</strong> 어노테이션에 nullable 속성을 넣어줬다</p>
<pre><code class="language-java">@Column
private float temp;</code></pre>
<p>이랬던 코드가</p>
<pre><code class="language-java">@Column(nullable = true)
private float temp;</code></pre>
<p>요래 됐슴다</p>
<p>하지만 오류는 여전히...</p>
<br>

<p>그래서 다시 구글링 시작!!</p>
<p>열심히 찾아본 결과</p>
<p>JPA는 entity에서 필드를 원시형 데이터 타입을 한다. </p>
<ul>
<li><h4 id="jpa를-사용하여-엔티티를-저장할-때-원시-타입에-null-값을-할당-하려고-하면-오류가-발생한다">JPA를 사용하여 엔티티를 저장할 때, 원시 타입에 NULL 값을 할당 하려고 하면 오류가 발생한다.</h4>
</li>
</ul>
<br>

<br>

<h2 id="어떻게-해결해">어떻게 해결해?</h2>
<hr>
해결 방법은 진짜 간단했다.

<p>원시형 데이터를 Wrapper 타입의 데이터 형태로 바꾸면 된다.</p>
<pre><code class="language-java">int &gt; Integer
float &gt; Float
double &gt; Double
boolean &gt; Boolean
long &gt; Long</code></pre>
<p>이런식으로!</p>
<p>그래서 나도 싹 바꿨다.
그 많은 컬럼을...</p>
<pre><code class="language-java">    //바이탈 넘버
        @Id
        @Column
        private int vitalnum;

        // 환자번호
        @Column
        private int patientnum;

        // 패혈증 수치
        @Column
        private Float sepsisscore;

        // 검사 일시 default값 지정
        @Column
        private String sepdate;
        // 산소포화도
        @Column(nullable = true)
        private Float o2sat;

        // 체온
        @Column(nullable = true)
        private Float temp;
        // 수축기 혈압
        @Column(nullable = true)
        private Integer sbp;
        // 이완기 혈압
        @Column(nullable = true)
        private Integer dbp;
        //호흡수
        @Column(nullable = true)
        private Integer resp;
        // 심장박동수
        @Column(nullable = true)
        private Integer hr;
        // 평균 동맥압 평균 혈압
        @Column(nullable = true)
        private Float map;

        //이산화 탄소 농도
        @Column(nullable = true)
        private Float etco2;

        //과잉 탄산염
        @Column(nullable = true)
        private Float baseexcess;

        //탄산수소이온
        @Column(nullable = true)
        private Float hco3;

        //흡기 손소분율
        @Column(nullable = true)
        private Float fio2;

        //산성 또는 알카리성 정도를 나타내는 척도
        @Column(nullable = true)
        private Float ph;

        //이산화 탄소 분압
        @Column(nullable = true)
        private Float paco2;

        //동맥혈의 산소포화도
        @Column(nullable = true)
        private Float sao2;

        //간 기능을 평가하기위해 사용하는 효소
        @Column(nullable = true)
        private Float ast;

        //신장기능 평가
        @Column(nullable = true)
        private Float bun;

        //알카리성 인산효소
        @Column(nullable = true)
        private Float alkalinephos;

        //칼슘
        @Column(nullable = true)
        private Float calcium;

        //염소이온
        @Column(nullable = true)
        private Float chloride;

        //크레아틴
        @Column(nullable = true)
        private Float creatinine;

        //직접빌리루빈
        @Column(nullable = true)
        private Float bilirubindirect;

        //혈당
        @Column(nullable = true)
        private Float glucose;

        //라틱산
        @Column(nullable = true)
        private Float lactate;

        //마그네슘
        @Column(nullable = true)
        private Float magnesium;

        //인
        @Column(nullable = true)
        private Float phosphate;

        //칼륨
        @Column(nullable = true)
        private Float potassium;

        //총 빌리루빈
        @Column(nullable = true)
        private Float bilirubintotal;

        //트로포닌
        @Column(nullable = true)
        private Float troponini;

        //헤마토크리트
        @Column(nullable = true)
        private Float hct;

        //헤모글로빈
        @Column(nullable = true)
        private Float hgb;

        //부분혈장응고시간
        @Column(nullable = true)
        private Float ptt;

        //백혈구 수
        @Column(nullable = true)
        private Float wbc;

        //혈액응고 역할 단백질
        @Column(nullable = true)
        private Float fibrinogen;

        //혈소판 수
        @Column(nullable = true)
        private Float platelets;</code></pre>
<p>이렇게 하니까 오류 해결!</p>
<br>

<h3 id="정리">정리</h3>
<hr>
jpa에서 원시형 데이터 타입은 null 값을 받지 못하기 때문에 wrapper 타입으로 필드의 타입을 지정해줘야한다.]]></description>
        </item>
        <item>
            <title><![CDATA[하노이의탑]]></title>
            <link>https://velog.io/@jang_tissue/%ED%95%98%EB%85%B8%EC%9D%B4%EC%9D%98%ED%83%91</link>
            <guid>https://velog.io/@jang_tissue/%ED%95%98%EB%85%B8%EC%9D%B4%EC%9D%98%ED%83%91</guid>
            <pubDate>Mon, 05 Feb 2024 15:05:04 GMT</pubDate>
            <description><![CDATA[<h2 id="문제-설명">문제 설명</h2>
<hr>
하노이 탑(Tower of Hanoi)은 퍼즐의 일종입니다. 세 개의 기둥과 이 기동에 꽂을 수 있는 크기가 다양한 원판들이 있고, 퍼즐을 시작하기 전에는 한 기둥에 원판들이 작은 것이 위에 있도록 순서대로 쌓여 있습니다. 게임의 목적은 다음 두 가지 조건을 만족시키면서, 한 기둥에 꽂힌 원판들을 그 순서 그대로 다른 기둥으로 옮겨서 다시 쌓는 것입니다.

<p>한 번에 하나의 원판만 옮길 수 있습니다.
큰 원판이 작은 원판 위에 있어서는 안됩니다.
하노이 탑의 세 개의 기둥을 왼쪽 부터 1번, 2번, 3번이라고 하겠습니다. 1번에는 n개의 원판이 있고 이 n개의 원판을 3번 원판으로 최소 횟수로 옮기려고 합니다.</p>
<p>1번 기둥에 있는 원판의 개수 n이 매개변수로 주어질 때, n개의 원판을 3번 원판으로 최소로 옮기는 방법을 return하는 solution를 완성해주세요.</p>
<br>
<br>

<h2 id="정답코드">정답코드</h2>
<pre><code class="language-java">
import java.util.*;

class Solution {
    static List&lt;int[]&gt; history = new ArrayList();        
    public int[][] solution(int n) {
        hanoi(n, 0, 2, 1);
        int[][] answer = new int[history.size()][2];
        for (int i = 0; i &lt; history.size(); i++) {
            int[] position = history.get(i);
            answer[i] = position;
        }
        return answer;
    }

    void hanoi(int num, int from, int to, int other) {
        if (num == 0) return;
        hanoi(num - 1, from, other, to);
        history.add(new int[]{from + 1, to + 1});
        hanoi(num - 1, other, to, from);
    }
}</code></pre>
<h2 id="설명">설명</h2>
<p>입력 n은 탑의 층의 갯수 라고 할 수 있다.</p>
<p>hanoi 메소드는 첫 번째 인자로 층을 입력 받고 두 번째 인자로 해당층이 어디에 있는지, 세 번째 인자는 어디로 갈껀지, 마지막 인자는 보조 기둥을 설정한다</p>
<p>일단 n 이 0일때는 더이상 할 것이 없으니 그냥 리턴 한다</p>
<p>3층의 탑이라 했을때, 마지막 층을 목표 위치에 이동하기 위해서는 그 앞에 있는 원반이 other 위치에 있어야한다.</p>
<p>그런데 선행과정에서 2층이 다른 곳에위치 하기 위해서는 그 앞에 존재하는 층이 other 위치에 존재해야 한다.</p>
<p>즉, 1층을 옮기기 위해서는 2층을 옮겨야하는데, 2층을 옮기기 위해서는 그 앞층인 3층을 옮겨야한다는 뜻이다.</p>
<p>따라서 재귀함수를 사용하면 쉽게 풀린다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Redis]]></title>
            <link>https://velog.io/@jang_tissue/Redis</link>
            <guid>https://velog.io/@jang_tissue/Redis</guid>
            <pubDate>Fri, 02 Feb 2024 10:38:17 GMT</pubDate>
            <description><![CDATA[<h1 id="redis">Redis?</h1>
<hr>

<p><img src="https://velog.velcdn.com/images/jang_tissue/post/e53b354d-a0a8-49f7-b122-f919f8dfc3c2/image.png" alt=""></p>
<p>Redis란 Remote Dictionary Server의 약자로 일종의 DBMS이다.</p>
<p>하지만 우리가 자주 사용하는 Orcle, MySQL처럼 관계형 데이터베이스(RDBMS)가 아닌 Dictionary 이름 그대로 Key와 Velue로 이루어진 비관계형 DBMS 이다.</p>
<br>

<h2 id="특징-1--자료구조">특징 1 : 자료구조</h2>
<hr>
Hash Map과 비슷하게 일반적인 데이터 타입 뿐만이 아니라 List, Hash, set 등 과 같은 다양한 자료구조를 사용할 수 있다는 것이 하나의 특징이다.

<p><img src="https://velog.velcdn.com/images/jang_tissue/post/42f64106-cd1e-4dbb-9805-e1f5df3c4646/image.png" alt=""></p>
<p>이러한 자료구조들을 사용할 수 있으며 코드로 예를 들면</p>
<pre><code class="language-java">
import redis.clients.jedis.Jedis;

public class RedisListExample {
    public static void main(String[] args) {
        // Redis 서버에 연결
        Jedis jedis = new Jedis(&quot;localhost&quot;);

        // 리스트에 값 추가
        jedis.lpush(&quot;mylist&quot;, &quot;Java&quot;);
        jedis.lpush(&quot;mylist&quot;, &quot;Python&quot;);
        jedis.lpush(&quot;mylist&quot;, &quot;JavaScript&quot;);

        // 리스트의 모든 값 가져오기
        System.out.println(&quot;All values in the list:&quot;);
        System.out.println(jedis.lrange(&quot;mylist&quot;, 0, -1));

        // Redis 연결 종료
        jedis.close();
    }
}</code></pre>
<p>이 코드는 Java에서 Redis을 사용하는데 value 값을 리스트 형태로 저장하는 코드이다</p>
<p>key 값은 mylist 이고,value 값은 Java, Python, JavaScript가 순차적으로 들어가게 된다.</p>
<p>이 결과로는 </p>
<pre><code class="language-java">All values in the list:
[JavaScript, Python, Java]</code></pre>
<p>이렇게 List 형식으로 출력된다.</p>
<p>다양한 자료구조로 저장할 수 있으니 필요할때 다르게 사용해서 저장해놓으면 된다.</p>
<br>

<h2 id="특징-2--인메모리-구조">특징 2 : 인메모리 구조</h2>
<hr>

<p>또 다른 특징으로 Redis는 데이터를 캐시형식으로 메모리에 저장하는 인메모리 데이터 구조이다.</p>
<p>인메모리 형태란 일반적으로 하드디스크에 데이터를 저장하는 것이 아니라 메모리에 저장하는 형태라는 것이다.</p>
<p>이 특징을 이해하기 위해서는 &#39;캐시&#39;(cache) 개념을 이해해야한다.</p>
<p>캐시는 나중에 요청이 들어올 결과를 미리 저장해두었다가 빠르게 클라이언트에게 데이터를 제공하는 것을 말한다.</p>
<p>즉, 자주 요청되는 결과를 메모리에 저장해두고 해당 요청이 들어오면 따로 DB나 api 참조 없이 cache에 접근하여 요청을 처리한다. </p>
<p>이 방식의 장점은 메모리에서 값을 출력하기 때문에 속도가 하드디스크에 접근하는 것 보다 몇백배 빠르다는 것이다.</p>
<p>즉, Redis는 이러한 캐시의 방식을 통해 데이터를 저장하는 &#39;인메모리 데이터 구조&#39;를 사용하기 때문에 다른 DBMS에 비해 속도가 매우 빠르다는 장점이 있다.</p>
<p>하지만 메모리에 데이터를 저장한다면 매우 빠를지언정 가장 큰 단점이 있다. </p>
<p>메모리의 &#39;휘발성&#39;이다.</p>
<p>만약 컴퓨터가 꺼진다면 메모리에 존재하던 데이터는 모두 삭제되기 때문에 만약 데이터를 따로 저장해놓지 않았다면 데이터 유실 현상이 일어나게 될 것이다.</p>
<p>이러한 단점을 극복 하기 위한 방법이 AOF와 RDB이다.</p>
<h4 id="aof--append-only-file">AOF : Append Only File</h4>
<p>이 방법은 redis에서 발생하는 모든 write/update 연산 자체를 모두 log 파일에 기록한다.</p>
<p>이러한 방법으로 인해 메모리가 삭제되더라도 파일에 기록되어 있기 때문에 데이터 유실이 일어나지 않는다.</p>
<h4 id="rdb--snapshotting">RDB : Snapshotting</h4>
<p>이 방법은 순간적으로 메모리에 있는 내용을 캡쳐하듯이 찍고 Disk에 전체를 옮겨 담는 방식이다.</p>
<br>
이러한 방식으로 메모리의 휘발성을 방지하고 영속성을 유지한다.

<p>하지만 AOF는 매번 파일에 기록하기 때문에 성능이 저하되는 단점이 있고, RDB방식은 disk에 데이터를 옮기기 전에 메모리에서 정보가 휘발된다면 데이터가 유실될 수 있는 단점이 존재한다.</p>
<p>이러한 단점 때문에 많은 기업에서 성능이 뛰어난 Redis를 주 저장장치로 하지 않고 cache 역할을 하는 서브 저장장치로 이용한다.</p>
<br>

<h2 id="why-redis">Why Redis?</h2>
<hr>


<p>그렇다면 기업들은 이 Redis를 왜 사용할까?</p>
<p>약 100만의 트래픽이 발생한다고 하자.</p>
<p>하지만 이렇게 많은 트래픽에서 모든 http 메소드가 실행되지는 않는다.</p>
<p>서버에 정보를 요청하는 Get 메소드가 대부분인데 약 90% 라고 했을 때 이 요청 전부를 DB와 연동한다면 비용이 매우 많이 들 것이다. </p>
<p>하지만 Redis를 DB와 연동하는 서버 바로 앞에 위치 시켜 사용한다면 Cache 방식을 통해 Get 요청에 대한 정보를 미리 메모리에 저장하여 클라이언트에게 훨씬 빠르게 데이터를 전달할 수 있으며, 실제 DB에서 발생하는 비용이 10%까지 줄어들 수 있을 것이다.</p>
<br>

<h2 id="현재-프로젝트에-사용한다면">현재 프로젝트에 사용한다면?</h2>
<hr>
현재 우리 프로젝트는 입력된 환자의 데이터를 보여주는 기능이 가장 중요하다.

<p>MySQL를 사용하는 현재 DB에 저장된 데이터가 많을수록, 조회하려는 날짜가 길 수록 출력해야하는 데이터가 많아질 것이고 이에따라 서버에 부하가 걸려 속도가 느려질 것이다.</p>
<p>Redis가 매우 성능이 좋은 편이지만 데이터 유실에 대한 위험성이 존재하는 것은 확실하다.
우리 프로젝트는 환자의 생체 데이터를 다루기 때문에 성능적인 측면을 위해 이러한 비안정성을 내버려 둘 수 없다.</p>
<p>때문에 우리 프로젝트에 적용하기 위해서는 다른 기업들과 비슷하게 RDBMS를 주 DB로 하고, Redis를 보조 DB 서버로 하여 적용하면 유저가 환저 정보를 출력하려했을때 Redis를 먼저 탐색하여 데이터가 있다면 매우 빠른 속도로 환자의 생체 데이터를 출력해줄 것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스_타겟넘버]]></title>
            <link>https://velog.io/@jang_tissue/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4%ED%83%80%EA%B2%9F%EB%84%98%EB%B2%84</link>
            <guid>https://velog.io/@jang_tissue/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4%ED%83%80%EA%B2%9F%EB%84%98%EB%B2%84</guid>
            <pubDate>Thu, 01 Feb 2024 08:41:07 GMT</pubDate>
            <description><![CDATA[<h2 id="문제-설명">문제 설명</h2>
<hr>

<p>n개의 음이 아닌 정수들이 있습니다. 이 정수들을 순서를 바꾸지 않고 적절히 더하거나 빼서 타겟 넘버를 만들려고 합니다. 예를 들어 [1, 1, 1, 1, 1]로 숫자 3을 만들려면 다음 다섯 방법을 쓸 수 있습니다.</p>
<p>사용할 수 있는 숫자가 담긴 배열 numbers, 타겟 넘버 target이 매개변수로 주어질 때 숫자를 적절히 더하고 빼서 타겟 넘버를 만드는 방법의 수를 return 하도록 solution 함수를 작성해주세요.</p>
<pre><code class="language-java">class Solution {
    int answer;
    public int solution(int[] numbers, int target) {
        answer = 0;
        dfs(0, 0, numbers, target);
        return answer;
    }
    void dfs(int n, int sum, int[] numbers, int target) {
        if(n == numbers.length) {
            if(sum == target) 
                answer++;
            return;
        }

        dfs(n + 1, sum + numbers[n], numbers, target);
        dfs(n + 1, sum - numbers[n], numbers, target);
    }
}</code></pre>
<p><img src="https://velog.velcdn.com/images/jang_tissue/post/da50c62b-f066-4f99-a3d9-db346501dd33/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스_이진 변환 반복하기]]></title>
            <link>https://velog.io/@jang_tissue/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4%EC%9D%B4%EC%A7%84-%EB%B3%80%ED%99%98-%EB%B0%98%EB%B3%B5%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@jang_tissue/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4%EC%9D%B4%EC%A7%84-%EB%B3%80%ED%99%98-%EB%B0%98%EB%B3%B5%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 31 Jan 2024 08:19:58 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/jang_tissue/post/c87d3d4e-5c96-41e3-8570-acab05ea119e/image.png" alt=""></p>
<h3 id="문제-설명">문제 설명</h3>
<p>0과 1로 이루어진 어떤 문자열 x에 대한 이진 변환을 다음과 같이 정의합니다.</p>
<p>x의 모든 0을 제거합니다.
x의 길이를 c라고 하면, x를 &quot;c를 2진법으로 표현한 문자열&quot;로 바꿉니다.
예를 들어, x = &quot;0111010&quot;이라면, x에 이진 변환을 가하면 x = &quot;0111010&quot; -&gt; &quot;1111&quot; -&gt; &quot;100&quot; 이 됩니다.</p>
<p>0과 1로 이루어진 문자열 s가 매개변수로 주어집니다. s가 &quot;1&quot;이 될 때까지 계속해서 s에 이진 변환을 가했을 때, 이진 변환의 횟수와 변환 과정에서 제거된 모든 0의 개수를 각각 배열에 담아 return 하도록 solution 함수를 완성해주세요.</p>
<h3 id="정답코드">정답코드</h3>
<pre><code class="language-java">
class Solution {
  public int[] solution(String s) {
    int[] answer = new int[2];

    while(s.length() &gt; 1) {

      int cntOne = 0;
      for(int i=0; i&lt;s.length(); i++) {

        if(s.charAt(i) == &#39;0&#39;) answer[1]++;
        else cntOne++;
      }

      s = Integer.toBinaryString(cntOne);
      answer[0]++;
    }

    return answer;
  }
}</code></pre>
<h3 id="설명">설명</h3>
<p>처음엔 문자열을 char형으로 바꿔 하나하나 값을 빼고 더하는 방식으로 하려고 했지만 코드가 상당히 복잡해지는 것을 알 수 있었다.</p>
<p>그래서 문자열 처리를 따로 하지 않고 해당 문자열의 인덱스 번호가 0이면 answer 의 1번 인덱스 에 +1 씩 더하고 1이라면 갯수를 센다</p>
<p>만약 1이라면 one 변수에 1 씩 더하여 결과 값을 2진수로 변환한 값을 다시 문자열 s에 저장하여 반복문을 다시 시작한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스_추억점수]]></title>
            <link>https://velog.io/@jang_tissue/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4%EC%B6%94%EC%96%B5%EC%A0%90%EC%88%98</link>
            <guid>https://velog.io/@jang_tissue/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4%EC%B6%94%EC%96%B5%EC%A0%90%EC%88%98</guid>
            <pubDate>Tue, 30 Jan 2024 06:51:30 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/jang_tissue/post/1eb67ae4-bc5a-4685-aaed-9fa51d1a35a1/image.png" alt=""></p>
<h3 id="문제-설명">문제 설명</h3>
<p>사진들을 보며 추억에 젖어 있던 루는 사진별로 추억 점수를 매길려고 합니다. 사진 속에 나오는 인물의 그리움 점수를 모두 합산한 값이 해당 사진의 추억 점수가 됩니다. 예를 들어 사진 속 인물의 이름이 [&quot;may&quot;, &quot;kein&quot;, &quot;kain&quot;]이고 각 인물의 그리움 점수가 [5점, 10점, 1점]일 때 해당 사진의 추억 점수는 16(5 + 10 + 1)점이 됩니다. 다른 사진 속 인물의 이름이 [&quot;kali&quot;, &quot;mari&quot;, &quot;don&quot;, &quot;tony&quot;]이고 [&quot;kali&quot;, &quot;mari&quot;, &quot;don&quot;]의 그리움 점수가 각각 [11점, 1점, 55점]]이고, &quot;tony&quot;는 그리움 점수가 없을 때, 이 사진의 추억 점수는 3명의 그리움 점수를 합한 67(11 + 1 + 55)점입니다.</p>
<h3 id="코드">코드</h3>
<pre><code class="language-java">import java.util.HashMap;

class Solution {
    public int[] solution(String[] name, int[] yearning, String[][] photo) {
        int[] answer = new int[photo.length];
        HashMap&lt;String, Integer&gt; map = new HashMap&lt;&gt;();
        for(int i = 0; i&lt;name.length; i++) {
            map.put(name[i], yearning[i]);
            System.out.println(map);
        }
        for(int i=0; i&lt;photo.length;i++) {
            int result = 0;
            for(int j=0; j&lt;photo[i].length; j++) {
                if(map.containsKey(photo[i][j]))
                result += map.get(photo[i][j]);
            }
            answer[i] = result;

        }
        return answer;
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[머신러닝_이미지 인식 실습(손 인식해서 마스크 씌우기)]]></title>
            <link>https://velog.io/@jang_tissue/%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%9D%B8%EC%8B%9D-%EC%8B%A4%EC%8A%B5%EC%86%90-%EC%9D%B8%EC%8B%9D%ED%95%B4%EC%84%9C-%EB%A7%88%EC%8A%A4%ED%81%AC-%EC%94%8C%EC%9A%B0%EA%B8%B0</link>
            <guid>https://velog.io/@jang_tissue/%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%9D%B8%EC%8B%9D-%EC%8B%A4%EC%8A%B5%EC%86%90-%EC%9D%B8%EC%8B%9D%ED%95%B4%EC%84%9C-%EB%A7%88%EC%8A%A4%ED%81%AC-%EC%94%8C%EC%9A%B0%EA%B8%B0</guid>
            <pubDate>Tue, 23 Jan 2024 08:38:36 GMT</pubDate>
            <description><![CDATA[<h1 id="코드">코드</h1>
<pre><code class="language-python"># 캠 연결하기
import cv2
import mediapipe as mp
import numpy as np
from sklearn.neighbors import KNeighborsClassifier

# 스파이더맨 이미지 관련 코드
spider = cv2.imread(&#39;./data/spider.jpg&#39;)
spider = cv2.resize(spider, (250,250))
mask2gray_spider = cv2.cvtColor(spider, cv2.COLOR_RGB2GRAY)
_, mask_b_spider = cv2.threshold(mask2gray_spider, 200, 255, cv2.THRESH_BINARY)
mask_b_inv_spider = cv2.bitwise_not(mask_b_spider)
img_fg_spider = cv2.bitwise_and(spider,spider,mask = mask_b_inv_spider) 

# 마스크 이미지 관련 코드
mask = cv2.imread(&#39;./data/mask.jpg&#39;)# 225,225
mask2gray_mask = cv2.cvtColor(mask, cv2.COLOR_RGB2GRAY)
_, mask_b_mask = cv2.threshold(mask2gray_mask, 250, 255, cv2.THRESH_BINARY)
mask_b_inv_mask = cv2.bitwise_not(mask_b_mask)
img_fg_mask = cv2.bitwise_and(mask,mask,mask = mask_b_inv_mask) 

# 아이언맨
iron = cv2.imread(&#39;./data/ironman.jpg&#39;)# 225,225
mask2gray_iron = cv2.cvtColor(iron, cv2.COLOR_RGB2GRAY)
_, mask_b_iron = cv2.threshold(mask2gray_iron, 200, 255, cv2.THRESH_BINARY)
mask_b_inv_iron = cv2.bitwise_not(mask_b_iron)
img_fg_iron = cv2.bitwise_and(iron,iron,mask = mask_b_inv_iron) 

# 헐크
hulk = cv2.imread(&#39;./data/hulk.jpg&#39;)# 225,225
mask2gray_hulk = cv2.cvtColor(hulk, cv2.COLOR_RGB2GRAY)
_, mask_b_hulk = cv2.threshold(mask2gray_hulk, 200, 255, cv2.THRESH_BINARY)
mask_b_inv_hulk = cv2.bitwise_not(mask_b_hulk)
img_fg_hulk = cv2.bitwise_and(hulk,hulk,mask = mask_b_inv_hulk) 

# 선글라스 이미지 관련 코드
sun = cv2.imread(&#39;./data/sunglass.jpg&#39;)# 225,150 &gt; 180,120
sun = cv2.resize(sun,(180,120))
mask2gray_sun = cv2.cvtColor(sun, cv2.COLOR_RGB2GRAY)
_, mask_b_sun = cv2.threshold(mask2gray_sun, 200, 255, cv2.THRESH_BINARY)
mask_b_inv_sun = cv2.bitwise_not(mask_b_sun)
img_fg_sun = cv2.bitwise_and(sun,sun,mask = mask_b_inv_sun) 

# 마스크이미지에서 사용할 영역의 값만 추출
video = cv2.VideoCapture(0)
cnt = 0

fps = 20
fcc = cv2.VideoWriter_fourcc(*&#39;DIVX&#39;)
width = int(video.get(3))
height = int(video.get(4))

# 인식 가능한 11가지 동작
gesture = {
    0:&#39;fist&#39;, 1:&#39;one&#39;, 2:&#39;two&#39;, 3:&#39;three&#39;, 4:&#39;four&#39;, 5:&#39;five&#39;,
    6:&#39;six&#39;, 7:&#39;rock&#39;, 8:&#39;spiderman&#39;, 9:&#39;yeah&#39;, 10:&#39;ok&#39;,
}


# 동작 인식 모델 만들기(knn 모델)
file = np.genfromtxt(&#39;./data/gesture_train.csv&#39;,delimiter = &#39;,&#39;)
X = file[:, :-1].astype(np.float32)
y = file[:, -1].astype(np.float32)
knn = KNeighborsClassifier(n_neighbors = 3)
knn.fit(X,y)

# mediapipe 사용하기
# 손찾기 관련 기능 불러오기
mp_hands = mp.solutions.hands
# 손 그려주는 기능 불러오기
mp_drawing = mp.solutions.drawing_utils
# 손찾기 관련 세부 설정
hands = mp_hands.Hands(
    max_num_hands = 1, # 탐지할 최대 손의 갯수
    min_detection_confidence = 0.5, # 표시할 손의 최소 정확도
    min_tracking_confidence = 0.5 # 표시할 관절의 최소 정확도
)

mp_face = mp.solutions.face_mesh
# 특징점 찾기 세부 기능
face = mp_face.FaceMesh(
    min_detection_confidence = 0.5, # 얼굴 표현할 최소 정확도
    min_tracking_confidence = 0.5 # 특징점 표현할 최소 정확도
)

video = cv2.VideoCapture(0)
while video.isOpened():
    ret, img = video.read()
    img = cv2.flip(img,1)
    # 파이썬이 인식 잘 하도록 BGR &gt; RGB로 변경
    img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
    # 손 탐지하기
    result = hands.process(img)
    img = cv2.cvtColor(img,cv2.COLOR_RGB2BGR)
    if not ret:
        break
    # 찾은 손 표시하기
    if result.multi_hand_landmarks is not None:
        # print(result.multi_hand_landmarks)  
        # 이미지에 손 표현하기
        for res in result.multi_hand_landmarks:
            joint = np.zeros((21,3)) # 21개 관절, xyz값 저장할 배열 생성
            # enumerate = for문의 순서 표현
            # 관절 값 저장
            for j , lm in enumerate(res.landmark):
                joint[j] = [lm.x, lm.y, lm.z]
            # 연결할 관절 번호 가져오기
            v1 = joint[[0,1,2,3,0,5,6,7,0,9,10,11,0,13,14,15,0,17,18,19],:]
            v2 = joint[[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20],:]
            v = v2-v1 # 뼈의 값(x,y,z좌표값 &gt; 벡터값)
            # 유클리디안 길이로 변환(피타고라스)
            # 뼈의 값(직선 값)
            v = v / np.linalg.norm(v, axis = 1)[:, np.newaxis]
            # 뼈의 값으로 뼈사이의 각도 구하기, 변화값이 큰 15개
            angle = np.arccos(np.einsum(&#39;nt,nt-&gt;n&#39;,
                v[[0,1,2,4,5,6,8,9,10,12,13,14,16,17,18],:], 
                v[[1,2,3,5,6,7,9,10,11,13,14,15,17,18,19],:]))
            # radian각도를 degree각도로 변경하기
            angle = np.degrees(angle)

            # 구한 각도를 knn모델에 예측시키기
            # 학습을 위한 타입 변경(2차원 array)
            X_pred = np.array([angle], dtype = np.float32)
            results = knn.predict(X_pred)
            idx = int(results)

            # 인식된 제스쳐 표현하기
            img_x = img.shape[1]
            img_y = img.shape[0]
            hand_x = res.landmark[0].x
            hand_y = res.landmark[0].y


            # 어떤 동작에 어떤 가면을 출력할지 정하기
            # 0 : 마스크, 7/8 : 스파이더맨, 9 : 선글라스
            if idx == 0:
                cv2.putText(img, text = &#39;Mask&#39;, 
                       org = ( int(hand_x * img_x) , int(hand_y * img_y)+20 ),
                       fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, color=(255, 255, 255), thickness=2
                       )
                face_result = face.process(img)
                if face_result.multi_face_landmarks is not None:
                    # 0번이 입의점
                    lip = face_result.multi_face_landmarks[0].landmark[0]
                    x_lip = int(lip.x * img.shape[1])
                    y_lip = int(lip.y * img.shape[0])
                    # cv2.circle(img, (x_nose,y_nose), 20, (0,0,255), cv2.FILLED)
                    # 프레임마다 실행하는 코드
                    try :
                        roi = img[y_lip -112 :y_lip + 113, x_lip -112 : x_lip + 113] # 어디 위치에 표현할건지
                        img_bg = cv2.bitwise_and(roi,roi,mask=mask_b_mask)
                        bg_fg = cv2.add(img_bg, img_fg_mask)
                        img[y_lip -112 :y_lip + 113, x_lip -112 : x_lip + 113] = bg_fg # 어디 위치에 표현할건지
                    except : 
                        pass


            elif idx == 7 or idx == 8:
                cv2.putText(img, text = &#39;SpiderMan&#39;, 
                       org = ( int(hand_x * img_x) , int(hand_y * img_y)+20 ),
                       fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, color=(255, 255, 255), thickness=2
                       )
                face_result = face.process(img)
                if face_result.multi_face_landmarks is not None:
                    nose = face_result.multi_face_landmarks[0].landmark[4]
                    x_nose = int(nose.x * img.shape[1])
                    y_nose = int(nose.y * img.shape[0])
                    # cv2.circle(img, (x_nose,y_nose), 20, (0,0,255), cv2.FILLED)
                    # 프레임마다 실행하는 코드
                    try :
                        roi = img[y_nose -125 :y_nose + 125, x_nose -125 : x_nose + 125] # 어디 위치에 표현할건지
                        img_bg = cv2.bitwise_and(roi,roi,mask=mask_b_spider)
                        bg_fg = cv2.add(img_bg, img_fg_spider)
                        img[y_nose -125 :y_nose + 125, x_nose -125 : x_nose + 125] = bg_fg # 어디 위치에 표현할건지
                    except : 
                        pass
            elif idx == 9:
                cv2.putText(img, text = &#39;Sunglass&#39;, 
                       org = ( int(hand_x * img_x) , int(hand_y * img_y)+20 ),
                       fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, color=(255, 255, 255), thickness=2
                       )

            elif idx == 5:
                cv2.putText(img, text = &#39;Iron Man&#39;, 
                       org = ( int(hand_x * img_x) , int(hand_y * img_y)+20 ),
                       fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, color=(255, 255, 255), thickness=2
                       )
                face_result = face.process(img)
                if face_result.multi_face_landmarks is not None:
                    nose = face_result.multi_face_landmarks[0].landmark[4]
                    x_nose = int(nose.x * img.shape[1])
                    y_nose = int(nose.y * img.shape[0])
                    # cv2.circle(img, (x_nose,y_nose), 20, (0,0,255), cv2.FILLED)
                    # 프레임마다 실행하는 코드
                    try :
                        roi = img[y_nose -112 :y_nose + 113, x_nose -112 : x_nose + 113] # 어디 위치에 표현할건지
                        img_bg = cv2.bitwise_and(roi,roi,mask=mask_b_iron)
                        bg_fg = cv2.add(img_bg, img_fg_iron)
                        img[y_nose -112 :y_nose + 113, x_nose -112 : x_nose + 113] = bg_fg # 어디 위치에 표현할건지
                    except : 
                        pass
            elif idx == 10:
                cv2.putText(img, text = &#39;Hulk&#39;, 
                       org = ( int(hand_x * img_x) , int(hand_y * img_y)+20 ),
                       fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, color=(255, 255, 255), thickness=2
                       )
                face_result = face.process(img)
                if face_result.multi_face_landmarks is not None:
                    nose = face_result.multi_face_landmarks[0].landmark[4]
                    x_nose = int(nose.x * img.shape[1])
                    y_nose = int(nose.y * img.shape[0])
                    # cv2.circle(img, (x_nose,y_nose), 20, (0,0,255), cv2.FILLED)
                    # 프레임마다 실행하는 코드
                    try :
                        roi = img[y_nose -112 :y_nose + 113, x_nose -112 : x_nose + 113] # 어디 위치에 표현할건지
                        img_bg = cv2.bitwise_and(roi,roi,mask=mask_b_hulk)
                        bg_fg = cv2.add(img_bg, img_fg_hulk)
                        img[y_nose -112 :y_nose + 113, x_nose -112 : x_nose + 113] = bg_fg # 어디 위치에 표현할건지
                    except : 
                        pass




            mp_drawing.draw_landmarks(img, res, mp_hands.HAND_CONNECTIONS)

    k = cv2.waitKey(30)
    if k == 49:
        break
    elif k == 50: # 캡쳐
        cv2.imwrite(f&#39;./data/cap_sun{cnt}.png&#39;, img, params=[cv2.IMWRITE_PNG_COMPRESSION, 0] )
        cnt += 1

    elif k == 51: # 녹화 시작
        out = cv2.VideoWriter(&#39;./data/sun.avi&#39;, fcc, fps, (width, height))
        record = True
    elif k == 52: #녹화 종료
        record = False
        out.release() # 동영상 녹화 종료(동영상 종료는 아님)
    cv2.imshow(&#39;hand&#39;,img)
video.release()
cv2.destroyAllWindows()</code></pre>
<h3 id="결과">결과</h3>
<hr>

<p><img src="https://velog.velcdn.com/images/jang_tissue/post/b9983ef4-fe21-4793-8024-f9fc5d5a41a0/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/jang_tissue/post/99d84f24-8b91-435e-92d2-014d3550a836/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 피보나치 수]]></title>
            <link>https://velog.io/@jang_tissue/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%ED%94%BC%EB%B3%B4%EB%82%98%EC%B9%98-%EC%88%98</link>
            <guid>https://velog.io/@jang_tissue/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%ED%94%BC%EB%B3%B4%EB%82%98%EC%B9%98-%EC%88%98</guid>
            <pubDate>Mon, 22 Jan 2024 08:27:48 GMT</pubDate>
            <description><![CDATA[<h3 id="문제-설명">문제 설명</h3>
<hr>

<p>피보나치 수는 F(0) = 0, F(1) = 1일 때, 1 이상의 n에 대하여 F(n) = F(n-1) + F(n-2) 가 적용되는 수 입니다.</p>
<p>예를들어</p>
<p>F(2) = F(0) + F(1) = 0 + 1 = 1
F(3) = F(1) + F(2) = 1 + 1 = 2
F(4) = F(2) + F(3) = 1 + 2 = 3
F(5) = F(3) + F(4) = 2 + 3 = 5
와 같이 이어집니다.</p>
<p>2 이상의 n이 입력되었을 때, n번째 피보나치 수를 1234567으로 나눈 나머지를 리턴하는 함수, solution을 완성해 주세요.</p>
<br>

<h3 id="정답-코드">정답 코드</h3>
<hr>

<pre><code class="language-java">import java.util.*;

class Solution {
    int[] fibo;

    public int solution(int n) {
        fibo = new int[n+1];

        return dfs(n);
    }

    private int dfs(int n) {
        if(fibo[n] &gt; 0) return fibo[n];

        if(n == 0) return 0;

        if(n == 1) return fibo[1] = 1;

        return fibo[n] = (dfs(n-1) + dfs(n-2))%1234567;
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 같은 숫자는 싫어]]></title>
            <link>https://velog.io/@jang_tissue/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EA%B0%99%EC%9D%80-%EC%88%AB%EC%9E%90%EB%8A%94-%EC%8B%AB%EC%96%B4</link>
            <guid>https://velog.io/@jang_tissue/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EA%B0%99%EC%9D%80-%EC%88%AB%EC%9E%90%EB%8A%94-%EC%8B%AB%EC%96%B4</guid>
            <pubDate>Fri, 19 Jan 2024 11:21:50 GMT</pubDate>
            <description><![CDATA[<h2 id="문제-설명">문제 설명</h2>
<hr>

<p>배열 arr가 주어집니다. 배열 arr의 각 원소는 숫자 0부터 9까지로 이루어져 있습니다. 이때, 배열 arr에서 연속적으로 나타나는 숫자는 하나만 남기고 전부 제거하려고 합니다. 단, 제거된 후 남은 수들을 반환할 때는 배열 arr의 원소들의 순서를 유지해야 합니다. 예를 들면,</p>
<p>arr = [1, 1, 3, 3, 0, 1, 1] 이면 [1, 3, 0, 1] 을 return 합니다.
arr = [4, 4, 4, 3, 3] 이면 [4, 3] 을 return 합니다.
배열 arr에서 연속적으로 나타나는 숫자는 제거하고 남은 수들을 return 하는 solution 함수를 완성해 주세요.</p>
<h2 id="정답-코드">정답 코드</h2>
<hr>

<pre><code class="language-java">import java.util.*;

public class Solution {
    public int[] solution(int []arr) {
        Stack&lt;Integer&gt; stack = new Stack&lt;&gt;();
        for (int num : arr) {
            if (stack.isEmpty() || stack.peek() != num) {
                stack.push(num);
            }
        }
        int[] answer = new int[stack.size()];
        for (int i = stack.size() - 1; i &gt;= 0; i--) {
            answer[i] = stack.pop();
        }
        return answer;
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[계산 복잡도]]></title>
            <link>https://velog.io/@jang_tissue/%EA%B3%84%EC%82%B0-%EB%B3%B5%EC%9E%A1%EB%8F%84</link>
            <guid>https://velog.io/@jang_tissue/%EA%B3%84%EC%82%B0-%EB%B3%B5%EC%9E%A1%EB%8F%84</guid>
            <pubDate>Thu, 18 Jan 2024 08:53:57 GMT</pubDate>
            <description><![CDATA[<h1 id="시-공간-복잡도">시 공간 복잡도</h1>
<p>먼저, 간단하게 말하자면 공간 복잡도는 코드가 실행됨에 있어서 얼마나 공간이 많이 차지하게 되는가를 따지는 방법이고,</p>
<p>시간 복잡도는 코드가 실행됨에 있어서 얼마나 시간이 오래 걸리냐의 방법이다.</p>
<p>복잡도를 나타내는 표기법은 크게 세가지로 구성되어있다.</p>
<ol>
<li>O (Big-O)</li>
<li>Ω (Omega)</li>
<li>Θ (Theta)</li>
</ol>
<p>가장 먼저 Big-O는 점근적 상한을 의미하는데, 최악의 상황을 고려하여 만들어진 것이다.</p>
<p>그 다음 Omega는 점근적 하한을 의미하는것으로, 최소한 이정도는 걸릴것이라는 보장을 나타낸다.</p>
<p>마지막 Theta는 점근적 상한과 점근적 하한의 정확한 값을 나타낸다.</p>
<p>이 3가지의 경우중 Theta를 사용하는것이 가장 좋을 것 같지만 계산이 너무 복잡해지기 때문에 잘 사용하지 않는다. 그리고 우리가 코드를 작성하여 서비스를 하는경우 최악의 상황을 더 중요하게 생각하기 때문에 대부분의 경우 Bic-O 표기법을 사용한다.</p>
<h3 id="시간-복잡도">시간 복잡도</h3>
<p>시간복잡도는 코드가 실행됨에 따라 다양한 크기의 입력이 주어졌을때 해당 코드가 얼마나 시간이 걸리는지를 나타내는 방법이다.</p>
<p>예시 코드로 살펴보자</p>
<pre><code class="language-java">public int time(int n) {

        int num = n; // 1

        num++; // 1

        int result = 0; // 1

        for(int i=0; i&lt;n; i++) { // i=0 : 1, result + n : n, i++ : n, i&lt;n : 1 &gt;&gt; 2n + 2
            result += n;
        }

        // 총 시간 : 2n + 5
        return result;
    }</code></pre>
<p>이 코드를 살펴보면,</p>
<pre><code class="language-java">        int num = n; // 1

        num++; // 1

        int result = 0; // 1</code></pre>
<p>어떠한 입력 n이 들어와도 1번의 계산만을 하는 경우 상수 시간이 걸린다.</p>
<p>이 3줄의 코드는 총 3 정도의 시간이 걸린다 할 수 있다.</p>
<p>반면</p>
<pre><code class="language-java">        for(int i=0; i&lt;n; i++) { // i=0 : 1, result + n : n, i++ : n, i&lt;n : 1 &gt;&gt; 2n + 2
            result += n;
        }</code></pre>
<p>이러한 반복문의 경우 n의 입력이 들어왔을때</p>
<p>int i=0 에서 1 정도의 시간,</p>
<p>i&lt;n 비교문에서 1 정도의 시간,</p>
<p>result += n; 을 n의 크기만큼 하기 때문에 n 정도의 시간,</p>
<p>i++을 n의 크기 만큼 사용하기 때문에 n 정도의 시간이 걸린다.</p>
<p>이 반복문의 경우 2n+2 정도의 시간이 걸린다</p>
<p>따라서 총 걸리는 시간은 (상수시간 3) + (반복문 시간 2n + 2)</p>
<p>총합 2n + 3의 선형 시간 복잡도가 나온다</p>
<p>하지만 계산 복잡도의 경우 O(2n+3)이라 표현하지 않는다.</p>
<p>n이 매우 커졌을때 상수항들은 상대적으로 무시될 정도로 작기 때문에 무시하게 되어 결국 O(n) 만 남게된다.</p>
<h3 id="공간-복잡도">공간 복잡도</h3>
<p>공간 복잡도는 크게 고정 공간과 임시공간 메모리의 크기를 나타낸다</p>
<p>여기서 말하는 고정공간은 코드를 작성할때 우리가 미리 선언한 배열의 길이라 생각하면 편하고, 임시공간은 공간이 한정적이지 않은 가변길이의 vector나 ArrayList 등을 생각하면 쉽다.</p>
<pre><code class="language-java">public void prac(int n) {

        //고정길이
        int[] arr1 = new int[10];

        for(int i=0; i&lt;10; i++) {
            arr1[i] = i;
        }

        //가변길이
        ArrayList&lt;Integer&gt; arr2 = new ArrayList&lt;Integer&gt;();

        for(int i=0; i&lt;n; i++) {
            arr2.add(n);
        }

    }</code></pre>
<p>이 메소드를 보면 고정길이 배열의 경우 10으로 공간이 결정되어있다.</p>
<p>이 배열은 어떠한 입력 n이 들어와도 크기는 바뀌지 않는다.</p>
<p>따라서 크기는 10으로 고정되어있기 때문에 상수로 표현하며,</p>
<p>표현식은 O(1)로 표현할 수 있다.</p>
<p>반면 가변길이 ArrayList의 경우 n이 입력되는 크기에 따라 배열의 크기가 변한다.</p>
<p>즉, 크기가 n이 되기 때문에 공간 복잡도를 나타내면 O(n)이라 할 수 있다.</p>
<h2 id="복잡도-계산-방법">복잡도 계산 방법</h2>
<p>복잡도 계산 방법은 크게 어렵지 않다.</p>
<p>일반적으로 입력되는 데이터에 영향을 받아 계산이 몇번 되는지에 따라 복잡도가 바뀐다.</p>
<p>앞서 설명했듯</p>
<pre><code class="language-java">public void met1(int n){
    int a = 0;
}

public void met2(int n){
    int a = n;
}

public void met3(int n){
    int a = 1;
    int b = 1;
    int c = a+b;
}</code></pre>
<p>met1 메소드는 n 값이 무엇이든 a는 전혀 영향을 받지 않기 때문에 상수 시간복잡도 O(1)이다.</p>
<p>그렇다면 met2 메소드는 입력되는 n에 변수 a가 영향을 받기 때문에 O(n)인가?</p>
<p>아니다. 결국 대입은 1번이고, 결국 계산은 1번이 실행되기 때문에 정답은 met1과 동일하게 O(1)이다.</p>
<p>met3는 계산이 3번인데 O(3)으로 표현하지 않는 이유는 최대의 데이터가 입력되는 경우 상수 시간은 고려되지 않기 때문에 O(1)로 표현한다.</p>
<pre><code class="language-java">public void met4(int n){
    int result = 0;
    for(int i=0; i&lt;n; i++){
            result += 1;
        }
}

public void met5(int n){
    int result = 0;
    for(int i=0; i&lt;n; i++){
            for(int j=0; j&lt;n; j++) {
                result += 1;
            }
        }
}

public void met6(int n){
    int result = 0;
    for(int i=0; i&lt;n; i++){
            result += 1;
        }
    for(int i=0; i&lt;n; i++){
            result += 1;
        }
}</code></pre>
<p>met4 메소드 경우 앞서 간단하게 설명했듯 입력된 n의 크기만큼 계산이 반복되기 때문에 시간 복잡도는 O(n)이 된다.</p>
<p>met5 메소드는 반복문이 중첩된 메소드이다. 입력된 값 n 만큼의 계산을 n번 반복하기 때문에 n^2이 된다. 따라서 시간복잡도는 O(n^2)이 된다.</p>
<p>그렇다면 met6 반복문은 어떨까.</p>
<p>입력된 값에 따라 반복문이 2개이기 때문에 met5 처럼 O(n^2) 인가?</p>
<p>정답은 아니다. 반복문이 중첩이 아니라 그냥 더해진것이기 때문에 2n 개의 계산이 이루어지고, 데이터가 커짐에 따라 상수항은 무시되어지기 때문에 met4와 동일하게 O(n)의 시간복잡도를 가지고 있다.</p>
<h3 id="1-o1">1. O(1)</h3>
<ul>
<li>입력값이 얼마나 되든 일정한 속도를 나타낸다</li>
<li>가장 효율적인 시간 복잡도를 가지고 있다.</li>
</ul>
<h3 id="2-olog-n">2. O(log n)</h3>
<ul>
<li>상수 시간에 이어 두 번째로 효율적인 시간 복잡도이다.</li>
<li>입력에 대해 로그 시간으로 실행된다.</li>
<li>매 반복마다 탐색 범위를 1/2로 감소 시키는 이진 탐색 알고리즘이 대표적이다</li>
</ul>
<h3 id="on">O(n)</h3>
<ul>
<li>O(log n) 다음으로 효율적인 시간복잡도이다.</li>
<li>선형시간 복잡도로 할 수 있으며, 입력된 데이터 만큼 동일한 비율로 늘어간다</li>
<li>처음부터 순차적으로 탐색하는 순차 탐색 알고리즘이 대표적이다</li>
</ul>
<h3 id="on-log-n">O(n log n)</h3>
<ul>
<li>O(log n) 을 n 번 반복하는 알고리즘에 대한 복잡도이다.</li>
<li>대표적으로 배열을 2개로 나누고 그 안에서 정렬을 진행하여  병합 정렬 알고리즘을 예로 들 수 있다</li>
</ul>
<h3 id="on2">O(n^2)</h3>
<ul>
<li>일반적으로 중첩 반복문에서 나타나는 시간 복잡도이다.</li>
<li>입력 값에 따라 가파르게 증폭된다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[YoloV5 개 고양이 분류]]></title>
            <link>https://velog.io/@jang_tissue/YoloV5-%EA%B0%9C-%EA%B3%A0%EC%96%91%EC%9D%B4-%EB%B6%84%EB%A5%98</link>
            <guid>https://velog.io/@jang_tissue/YoloV5-%EA%B0%9C-%EA%B3%A0%EC%96%91%EC%9D%B4-%EB%B6%84%EB%A5%98</guid>
            <pubDate>Wed, 17 Jan 2024 14:54:07 GMT</pubDate>
            <description><![CDATA[<h2 id="1-모델-import-받기">1. 모델 import 받기</h2>
<hr>

<p>모델은 우리가 학습을 시킨 모델을 사용한다.</p>
<p>그러기 위해서는 사이트에서 직접 사진을 가지고 데이터 라벨링을 통해 학습을 시켜야한다.
그 과정은 나중에 추가로 설명할것이다.</p>
<p>일단 Yolo를 사용하는 라이브러리를 받아준다.</p>
<pre><code class="language-python">import pathlib
temp = pathlib.PosixPath
pathlib.PosixPath = pathlib.WindowsPath

import torch # yolo 사용하는 라이브러리
import numpy as np
import cv2 # 이미지 다루는 라이브러리</code></pre>
<br>

<p>그 이후 내가 학습시킨 모델을 사이트에서 다운받고 load한다</p>
<pre><code class="language-python">model = torch.hub.load(&#39;ultralytics/yolov5&#39;, &#39;custom&#39;, path=&#39;./data/best.pt&#39; , force_reload=True)</code></pre>
<br>

<h2 id="이미지-검사">이미지 검사</h2>
<hr>

<pre><code class="language-python"># 이미지 불러오기
dog = cv2.imread(&#39;./data/dogs.png&#39;)

# 객체 검출하기
result = model(dog)
# ctrl + shift + &#39;-&#39; &gt; 커서 기준으로 셀 분할
# shift + m &gt; 커서 기준으로 셀 분할

# 6개의 값
# x1, y1, x2, y2, 정확도(confidence), class
detect = result.xyxyn[0].numpy()

#기존이미지 크기 
x = dog.shape[1]
y = dog.shape[0]

#여러마리 검출
for i in range(len(detect)):
    # 객체의 위치 계산
    x1 = int(detect[i,0] * x)
    y1 = int(detect[i,1] * y)
    x2 = int(detect[i,2] * x)
    y2 = int(detect[i,3] * y)
    # 객체 검출했으면 이미지에 표시하기
    cv2.rectangle(dog, (x1,y1) , (x2,y2) , (0,0,255), 2)


#이미지 띄우기
cv2.imshow(&#39;dog&#39;,dog)
cv2.waitKey(0)
cv2.destroyAllWindows()</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Queue 개념]]></title>
            <link>https://velog.io/@jang_tissue/Queue-%EA%B0%9C%EB%85%90</link>
            <guid>https://velog.io/@jang_tissue/Queue-%EA%B0%9C%EB%85%90</guid>
            <pubDate>Tue, 16 Jan 2024 08:54:46 GMT</pubDate>
            <description><![CDATA[<h1 id="queue">Queue</h1>
<p>java 1.5 이후로 들어온 Queue는 컬렉션 프레임 워크를 사용하는 인터페이스 중 하나이다.</p>
<p>데이터를 어떠한 방식으로 저장하고, 꺼내올 것인지를 정하는 방법의 일종이라 생각하면 이해가 빠를 것이다.</p>
<p>그 중 가장 간단하고 기본이 되는것이 <strong>Queue</strong> 이다. </p>
<p>Queue에 데이터를 입력하면 순차적으로 데이터가 저장이 된다.</p>
<p>그리고 저장된 데이터를 다시 꺼낼때 Queue의 특징이 나타나는데, 먼저 저장된 값들이 먼저 나오게 된다.</p>
<p>그림으로 표현하면</p>
<p><img src="https://prod-files-secure.s3.us-west-2.amazonaws.com/a0da910a-ee16-48d6-86d1-04259b381b87/870c8b1f-d85c-4c1d-9070-fcad286af6cd/Untitled.png" alt="Untitled"></p>
<p>이러한 그림이 나온다.</p>
<p>우리나라 말로는 선입선출이라 표현할 수 있고, 외국에서는 First In First Out의 앞글자를 따서 FIFO 방식이라 할 수 있다.</p>
<h3 id="활용-방법">활용 방법</h3>
<p>java에서 Queue를 사용하기 위해서는 import를 먼저 해줘야한다.</p>
<pre><code class="language-java">import java.util.LinkedList;
import java.util.Queue;</code></pre>
<p>Queue를 구현하는데 왜 LinkedList도 필요한지 궁금할것이다.</p>
<p>Queue는 기존에 있던것이 아니고 자바 1.5버전 이후에 들어온 것이기 때문에 구현체를 따로 만들어줘야한다 그래서 여러가지 프레임 워크로 으로 만들 수 있는데, 가장 많이 사용하는 프레임워크가 LinkedList이다.</p>
<p>큐도 순차적으로 되어있는것 같은데 왜 익숙한 ArrayList 프레임워크가 아니라 LinkedList로 만드는것일까?</p>
<p>여기서 ArrayList와 LinkedList 의 방식의 차이를 알면 이해가 빠르다.</p>
<p>먼저, LinkedList는 데이터가 순차적으로 되어있는 것 같지만 사실 데이터가 순차적으로 존재하는 것은 아니다. 단지 하나의 데이터가 이전 데이터의 주소값, 이후 데이터의 주소값, 그리고 데이터의 값만 들어있는 형태이다.</p>
<p>때문에 LinkedList는 값을 삭제, 추가할때 각 데이터의 주소값만 변경하면 되기 때문에 삽입과 삭제가 매우 빠르다.</p>
<p>반면, ArrayList 는 데이터가 순차적으로 존재한다. ArrayList는 조금 복잡한 구조로 움직인다. 가장 먼저 지정된 크기보다 더 많은 데이터가 들어올 경우는 더 큰 ArrayList를 생성하고 기존의 값들이 복사 되어 저장된 후 새로운 값이 들어오는 구조이다. 삽입과 삭제의 경우도 이와 비슷하게 복사를 통해 이루어진다.</p>
<p>때문에 Queue 인스턴스를 만들때는 보다 삽입과 삭제가 빠른 LinkedList를 사용하여 만든다.</p>
<p>Queue인스턴스 생성 방법</p>
<pre><code class="language-java">Queue&lt;E&gt; que = new LinkedList&lt;&gt;();</code></pre>
<p>일반적인 인스턴스 생성과 매우 흡사하지만 제너릭 방식으로 타입을 확정해주는것이 좋다.</p>
<h3 id="자주-사용하는-메소드">자주 사용하는 메소드</h3>
<pre><code class="language-java">Queue&lt;Integer&gt; que = new LinkedList&lt;&gt;();
        que.add(1);//값 입력 방법 1
        que.offer(2);//값 입력 방법 2
        que.add(3);
        que.offer(4);

        System.out.println(que.peek()); // 가장 앞에 있는 값을 가져옴. 삭제 X
        System.out.println(que);

        System.out.println(que.poll()); // 가장 앞에 있는 값을 가져옴. 삭재 O
        System.out.println(que);

        System.out.println(que.contains(3)); // 3이 들어있는지. 있으면 true, 없으면 false

        System.out.println(que.isEmpty()); // que가 비어있는지. 비어있으면 true, 아니면 false

        que.remove(2);// 해당 하는 값을 제거
        System.out.println(que);

        System.out.println(que.size()); // que의 크기

        que.clear();//que 데이터 전체 삭제
        System.out.println(que);</code></pre>
<p>위 코드의 출력은 이렇게 나온다</p>
<p><img src="https://prod-files-secure.s3.us-west-2.amazonaws.com/a0da910a-ee16-48d6-86d1-04259b381b87/00826618-0651-49e6-928a-7d066430065e/Untitled.png" alt="Untitled"></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Boot 사진 파일 저장, 출력]]></title>
            <link>https://velog.io/@jang_tissue/Spring-Boot-%EC%82%AC%EC%A7%84-%ED%8C%8C%EC%9D%BC-%EC%A0%80%EC%9E%A5-%EC%B6%9C%EB%A0%A5</link>
            <guid>https://velog.io/@jang_tissue/Spring-Boot-%EC%82%AC%EC%A7%84-%ED%8C%8C%EC%9D%BC-%EC%A0%80%EC%9E%A5-%EC%B6%9C%EB%A0%A5</guid>
            <pubDate>Fri, 12 Jan 2024 09:30:01 GMT</pubDate>
            <description><![CDATA[<p>페이지를 만들때 대부분 사진 파일 저장, 혹은 출력 기능을 만들어야한다.</p>
<p>이번에 설명할 것은 Spring Boot 에서 사진 파일을 저장, 출력하는 기능을 설명할것이다.</p>
<p>그다지 어렵지 않으니 잘 따라 오도록!</p>
<br>

<h3 id="게시글-작성">게시글 작성</h3>
<hr>

<h4 id="전체코드">전체코드</h4>
<pre><code class="language-jsp">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot;
    pageEncoding=&quot;UTF-8&quot;%&gt;

&lt;!-- context path를 &quot;cpath&quot;라는 이름으로 저장해두기 --&gt;
&lt;%@ taglib uri=&quot;http://java.sun.com/jsp/jstl/core&quot;  prefix=&quot;c&quot;%&gt;
&lt;% pageContext.setAttribute(&quot;cpath&quot;, request.getContextPath()); %&gt;    

&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD HTML 4.01 Transitional//EN&quot; &quot;http://www.w3.org/TR/html4/loose.dtd&quot;&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Forty by HTML5 UP&lt;/title&gt;
&lt;meta charset=&quot;utf-8&quot; /&gt;
&lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=UTF-8&quot;&gt;
&lt;!--[if lte IE 8]&gt;&lt;script src=&quot;${cpath}/assets/js/ie/html5shiv.js&quot;&gt;&lt;/script&gt;&lt;![endif]--&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;${cpath}/assets/css/main.css&quot; /&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;${cpath}/assets/css/board.css&quot; /&gt;
&lt;!--[if lte IE 9]&gt;&lt;link rel=&quot;stylesheet&quot; href=&quot;${cpath}/assets/css/ie9.css&quot; /&gt;&lt;![endif]--&gt;
&lt;!--[if lte IE 8]&gt;&lt;link rel=&quot;stylesheet&quot; href=&quot;${cpath}/assets/css/ie8.css&quot; /&gt;&lt;![endif]--&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;div id=&quot;board&quot;&gt;
    &lt;h1&gt;게시글 작성페이지&lt;/h1&gt;
        &lt;%--게시글 작성 form태그 --%&gt;
        &lt;%--데이터 전송에 필요한 form태그 3요소!! --%&gt;
        &lt;form action=&quot;${cpath}/member/write&quot; method=&quot;post&quot; enctype=&quot;multipart/form-data&quot;&gt;
            &lt;table id=&quot;list&quot;&gt;
                &lt;tr&gt;
                    &lt;td&gt;제목&lt;/td&gt;
                    &lt;td&gt;&lt;input name=&quot;title&quot; type=&quot;text&quot;&gt;&lt;/td&gt;
                &lt;/tr&gt;
                &lt;tr&gt;
                    &lt;td&gt;작성자&lt;/td&gt;
                    &lt;td&gt;&lt;input value=&quot;${user.email}&quot; name=&quot;writer&quot; type=&quot;text&quot;&gt;&lt;/td&gt;
                &lt;/tr&gt;
                &lt;tr&gt;
                    &lt;td&gt;이미지&lt;/td&gt;
                    &lt;td&gt;&lt;input name=&quot;file&quot; type=&quot;file&quot;&gt;&lt;/td&gt;
                &lt;/tr&gt;
                &lt;tr&gt;
                    &lt;td colspan=&quot;2&quot;&gt;내용&lt;/td&gt;
                &lt;/tr&gt;
                &lt;tr&gt;
                    &lt;td colspan=&quot;2&quot;&gt;&lt;textarea name=&quot;content&quot; rows=&quot;10&quot; style=&quot;resize: none;&quot;&gt;&lt;/textarea&gt;
                    &lt;/td&gt;
                &lt;/tr&gt;
                &lt;tr&gt;
                    &lt;td colspan=&quot;2&quot;&gt;&lt;input type=&quot;reset&quot; value=&quot;초기화&quot;&gt; &lt;input
                        type=&quot;submit&quot; value=&quot;작성하기&quot;&gt;&lt;/td&gt;
                &lt;/tr&gt;
            &lt;/table&gt;
        &lt;/form&gt;
    &lt;/div&gt;
    &lt;!-- Scripts --&gt;
    &lt;script src=&quot;${cpath}/assets/js/jquery.min.js&quot;&gt;&lt;/script&gt;
    &lt;script src=&quot;${cpath}/assets/js/jquery.scrolly.min.js&quot;&gt;&lt;/script&gt;
    &lt;script src=&quot;${cpath}/assets/js/jquery.scrollex.min.js&quot;&gt;&lt;/script&gt;
    &lt;script src=&quot;${cpath}/assets/js/skel.min.js&quot;&gt;&lt;/script&gt;
    &lt;script src=&quot;${cpath}/assets/js/util.js&quot;&gt;&lt;/script&gt;
    &lt;!--[if lte IE 8]&gt;&lt;script src=&quot;${cpath}/assets/js/ie/respond.min.js&quot;&gt;&lt;/script&gt;&lt;![endif]--&gt;
    &lt;script src=&quot;${cpath}/assets/js/main.js&quot;&gt;&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<br>

<p>이 jsp 파일에서는 form 태그가 가장 중요하다.</p>
<pre><code class="language-jsp">&lt;form action=&quot;${cpath}/member/write&quot; method=&quot;post&quot; enctype=&quot;multipart/form-data&quot;&gt;</code></pre>
<p>사진 파일은 그냥 저장되는 것이 아니라 String 형식으로 incoding을 진행한 뒤 파일이 전달된다.</p>
<p>그렇다면 jsp 파일에서 값을 전달받은 controller를 살펴보자</p>
<br>

<h4 id="write-mapping">write mapping</h4>
<pre><code class="language-java">    @PostMapping(&quot;/write&quot;)
    public String write(MultipartFile file,Tbl_board board) {
        // 1. 데이터 수집
        // 파일 저장하기
        // 랜덤한 문자열을 생성해서 파일 이름앞에 붙이기(uid)
        String uuid = UUID.randomUUID().toString();

        String filename = uuid +&quot;_&quot;+ file.getOriginalFilename();

        //파일을 저장하기 위한 경로 객체 만들기
        //Path : 경로 객체
        //Paths : 경로객체2
        Path path = Paths.get(savePath+filename);

        //해당 경로에 파일 저장
        try {
            file.transferTo(path);

            board.setImg(filename);

        }catch (Exception e){
            e.printStackTrace();
        }

        // 2. 로직 실행
        repo.save(board);

        // 3. view 실행

        return &quot;redirect:/member/list&quot;;



    }</code></pre>
<h4 id="부분코드-1">부분코드 1</h4>
<p>가장 먼저 눈에 띄는 코드는 </p>
<pre><code class="language-java">String uuid = UUID.randomUUID().toString();</code></pre>
<p>이 코드일 것이다.</p>
<p>카카오톡에서 사진을 받아 저장할때 사진 이름 앞에 이상한 문자가 함께 적혀 저장된 것을 한번쯤을 봤을 것이다.</p>
<p>그 기능은 사용자가 서버에 파일을 업로드 할때 이름이 중복되면 파일이 덮어씌워져버리니까, 그 것을 해결하기위해 파일 이름 앞에 랜덤하게 값을 붙히는 것이다.</p>
<p>이 역할을 하는 코드가 위에서 보여주는 코드이다.</p>
<br>

<h4 id="부분코드-2">부분코드 2</h4>
<p>그 다음으로 사용자가 서버에 파일을 업로드 하면 서버에서 해당 파일을 저장할때 경로를 잘 지정해야한다.</p>
<p>일반적으로 경로로는 application.properties에 정의해놓은 데이터를 가져올 수 있다.</p>
<pre><code class="language-java">    @Value(&quot;${save.path}&quot;) 
    private String savePath;</code></pre>
<p>이 코드는 savePath 변수에 이미 정의해놓은 경로가 들어간다.</p>
<pre><code class="language-java">        Path path = Paths.get(savePath+filename);

        //해당 경로에 파일 저장
        try {
            file.transferTo(path);

            board.setImg(filename);

        }catch (Exception e){
            e.printStackTrace();
        }</code></pre>
<p> 이 코드가 지정된 경로에 파일 이름으로 저장한 것이고, 또 board객체에 파일 이름을 set 하여 DB에 저장한 코드이다.</p>
<p> 마지막으로 redirect방식으로 전달하여 다시 list 페이지 요청을한다.</p>
<br>

<hr>

<br>

<h2 id="저장된-이미지-출력하기">저장된 이미지 출력하기</h2>
<hr>

<p>사용자가 게시글을 클릭하면 해당 게시글에 저장된 내용을 출력 해야한다.</p>
<h4 id="게시글-dto-코드">게시글 DTO 코드</h4>
<pre><code class="language-java">package com.smhrd.entity;

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;

import org.springframework.data.annotation.Persistent;

import lombok.Data;

@Entity
@Data
@Persistent
public class Tbl_board {

    //글번호
    //JPA 쓸 때, 참조 자료형으로 선언하는 것이 좋다.
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) //outo_increment 
    private Long idx;

    //제목
    @Column(length=100, nullable = false)
    private String title;

    //작성자(FK)
    //실제 컬럼이름은 &quot;컬럼명_참조받은컬럼명&quot; 지정됨
    @ManyToOne
    @JoinColumn(referencedColumnName = &quot;email&quot;)
    private Tbl_Member writer;

    //내용
    @Column(length = 1000, nullable = false)
    private String content;

    //이미지
    private String img;

    //작성일
    //default를 지정하는 법
    @Column(columnDefinition = &quot;datetime default now()&quot;, insertable = false, updatable = false)
    private Date indate;

    //조회수
    @Column(columnDefinition = &quot;int default 0&quot;, insertable=false)
    private Long count;

    //** 중요! 버그방지를 위해 이거 해야함  **
    //FK 를 사용하는 경우 toString 메소드를 직접 Override 할것
    @Override
    public String toString() {
        return &quot;Tbl_Board_toString_method&quot;;
    }
}
</code></pre>
<p>위 파일은 lombok 을 사용하고있다.</p>
<p>필드 내용으로는</p>
<p>게시글 idx
작성자
내용
이미지
조회수</p>
<p>이렇게 있다.</p>
<p>여기에 사용된 어노테이션을 살펴보겠다.</p>
<ol>
<li><p>idx
@Id // 해당 칼럼이 Primary key 라고 지정해준다.
@GeneratedValue(strategy = GenerationType.IDENTITY) // 행이 생성될때 자동으로 increment 가 되도록 해준다.</p>
</li>
<li><p>writer
@ManyToOne // Forign 키를 설정한다. 작성자 한명이 여러 게시글을 작성할 수 있기 때문에 ManyToOne 이다.
@ JoinColummn(referencedColumnName = &quot;email&quot;) // Member 테이블에 email을 참조한다는 뜻이다.</p>
</li>
<li><p>@Column(columnDefinition = &quot;datetime default now()&quot;, insertable = false, updatable = false) // 현재 시간을 저장해주는 칼럼이며 자료형부터 새로 작성해줘야한다. 그리고 입력 가능여부와 업데이트 가능 여부를 false로 설정하여 막아둔다.</p>
</li>
</ol>
<h4 id="boardcontroller-코드">BoardController 코드</h4>
<pre><code class="language-java">    @RequestMapping(&quot;/view&quot;)
    public String view(Long idx, Model model) {
        // 1. 데이터 수집
        // 2. 로직 실행
        //Optional&lt;&gt;값을 리턴하기 때문에 뒤에 .get()을 해줘야함
        Tbl_board board = repo.findById(idx).get();
        // 3. view 선택
        model.addAttribute(&quot;board&quot;,board);
        return &quot;member/viewBoard&quot;;
    }</code></pre>
<p>repo.findByID() 이 코드는 Optional&lt;&gt; 이라는 스프링만의 자료형으로 감싸서 리턴하기 때문에 .get() 메소드를 사용하여 DTO를 받아줘야한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Boot 게시판 CRUD 실습]]></title>
            <link>https://velog.io/@jang_tissue/Spring-Boot-%EA%B2%8C%EC%8B%9C%ED%8C%90-CRUD-%EC%8B%A4%EC%8A%B5</link>
            <guid>https://velog.io/@jang_tissue/Spring-Boot-%EA%B2%8C%EC%8B%9C%ED%8C%90-CRUD-%EC%8B%A4%EC%8A%B5</guid>
            <pubDate>Thu, 11 Jan 2024 08:56:11 GMT</pubDate>
            <description><![CDATA[<p>Spring Boot는 앞에서 했던 기본 Spring과 유사하지만 일부 기능들이 더 간단해졌다.</p>
<p>전체적으로 게시판 CRUD(Create, Read, Update, Delete)를 진행하면서 익숙해져보자.</p>
<p>우선, 우리는 jsp를 사용할 예정이기 때문에 viewname을 결정하는 preffix와 serffix를 설정해줘야한다. (참고로 maven을 활용해 필요한 라이브러리들을 다 받아주고 시작해야한다)</p>
<h3 id="jsp-사용을-위해-applicationproperties-파일-내부-작성">Jsp 사용을 위해 application.properties 파일 내부 작성</h3>
<pre><code>spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp
</code></pre><br>

<h2 id="tbl_member">Tbl_Member</h2>
<hr>

<p>일종의 DTO 이다.</p>
<h4 id="전체코드">전체코드</h4>
<pre><code class="language-java">package com.smhrd.entity;

import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Entity //이게 있어야 jpa가 인식해서 테이블을 만들어줌
@AllArgsConstructor // 전체 필드를 초기화하는 생성자
@NoArgsConstructor //기본생성자
@Data //Getter/Setter
public class Tbl_Member {    

    // email
    // @Column의 매개변수를 이용해서 컬럼의 상세한 내용 설정 가능
    // insertable 
    // updatable &gt;&gt; insert문 또는 update문이 자동완성 될 때, 포함할건지
    // insert into Tbl_member(email,pw,tel,address) values ...
    @Id
    @Column(length=50, updatable = false)
    private String email;

    // pw
    @Column(length=50, nullable=false)
    private String pw;

    // tel
    private String tel;

    // address
    private String address;

    @OneToMany(mappedBy = &quot;writer&quot;)
    private List&lt;Tbl_board&gt; board;


    //pk나 fk 처럼 관계가 정립되면 toString을 오버라이딩 해줘야함 (안그럼 오류!)
    @Override
    public String toString() {
        return &quot;Tbl_Member&quot;;
    }

}
</code></pre>
<p>이 클래스는 유저 맴버의 정보가 담길 클래스이다.
따라서 DB의 테이블에서 꺼낸 데이터를 담을 수 있도록 설계를 해야한다.</p>
<p>무조건 컬럼 명과 이 DTO의 필드명이 동일해야한다. </p>
<p>여기서 Spring Boot의 신기한 기능이 추가된다.</p>
<p>이전까지는 직접 DB에 쿼리문으로 테이블을 작성했다면, 이제는 <strong>OMR 방식</strong>이라 하여 이렇게 DTO 를 설계하기만 해도 테이블이 <strong>자동</strong>으로 생성된다.</p>
<h4 id="fk-설정코드">FK 설정코드</h4>
<pre><code class="language-java">
    @OneToMany(mappedBy = &quot;writer&quot;)
    private List&lt;Tbl_board&gt; board;</code></pre>
<p>1대 다 구조인지 , 다대 1 구조인지 잘 파악해서 작성해야한다.</p>
<p>또한 여기서 중요한게 pk나 fk 처럼 관계가 정립되면 toString 메소드를 오버라이딩 해줘야한다. (안그럼 오류가 발생함)</p>
<pre><code class="language-java">    @Override
    public String toString() {
        return &quot;Tbl_Member&quot;;
    }</code></pre>
<br>

<h2 id="memberrepository">MemberRepository</h2>
<hr>
Spring Boot에서 DB와 연결하는 DAO 역할을 하는것이 repository이다.

<pre><code class="language-java">import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

import com.smhrd.entity.Tbl_Member;

// MyBatis &gt;&gt; MyBatis:scan

@Repository //이 파일이 JPA Repository임을 명시, Scan되도록 함
public interface MemberRepository extends JpaRepository&lt;Tbl_Member, String&gt; {

    // Repository는 반드시 JpaRepository Interface를 상속 받아야함
    // &lt; &gt; == 제네릭 문법
    // JpaRepository&lt;T, ID&gt;
    // T : Table과 연결된 Entity
    // ID : Entity 내 PK(ID)의 자료형

    //JPA : No SQL, SQL문을 쓰지 말자
    //JpaRepository 안에 기본 CRUD 메소드가 정의 되어있음
    //hibernate라는 구현체가 테이블과, ID에 맞춰서 자동으로 구현

    // 특별한 조건을 가지는 메소드는 직접 구현
    // 1. 메소드 이름 규칙을 이용한 구현
    public Tbl_Member findByEmailAndPw(String email, String pw);

    // 2.@Query 어노테이션을 이용하여 직접SQL문을 작성하는 방법
    // 복잡한 쿼리문 작성하기 힘듦(join, 서브쿼리)
    // JPA 단일보다는 MYBatis를 겸용하는 경우가 많다
    //@Query(&quot;select * from dbl_member where email = :email and pw = :pw&quot;)
    //public Tbl_Member login(String email, String pw); //JPA 에서는 변수 하나하나 따로따로 넣어줘야함

}</code></pre>
<p>@Repository 를 사용하여 이 파일이 Respository임을 명시하고 메모리에 scan 되도록 한다.</p>
<p>JPA는 sql 문을 쓰지 않기 위해 메소드 이름을 이용하여 직접 구현한다.</p>
<ul>
<li>hibernate 라는 구현체가 테이블과, ID에 맞춰서 자동으로 구현한다.</li>
</ul>
<p>어노테이션을 이용하여 직접 sql문을 작성하는 경우 JPA에서는 꼭 변수명을 다 작성해줘야한다.</p>
<br>

<h2 id="membercontroller">MemberController</h2>
<hr>

<h4 id="전체코드-1">전체코드</h4>
<pre><code class="language-java">package com.smhrd.controller;

import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import com.smhrd.entity.Tbl_Member;
import com.smhrd.repository.MemberRepository;

@Controller
@RequestMapping(&quot;/guest&quot;)
public class MemberController {

    //Repository 주입
    //Interface &gt; 추상메소드 밖에 없음 &gt;&gt; 객체 생성 불가
    @Autowired // Spring 메모리(Spring Container) 내에서 넣어줄 수 있는 객체를 알아서 찾아서 넣어달라
    private MemberRepository repo;

    //http://localhost:8088/{contextPath}/{URLMapping}
    // http://localhost:8088/boot/main

    // ~~~/main 이라고 요청했을 때, main.jsp로 이동
    @RequestMapping(&quot;/main&quot;)
    public String goMain() {

        return &quot;guest/main&quot;;
    }

    @RequestMapping(&quot;/join&quot;)
    public String join(Tbl_Member member) {
        //1. 데이터 수집

        //2. 로직 실행
        // 수집한 정보를 DB에 저장하는 코드
        //Jpa의 save
        // 1. id를 기준으로 select
        //2. 있으면 &gt;&gt; update
        //     없으면 &gt;&gt; insert
        repo.save(member);

        //3. View 선택 / 데이터 응답
        return &quot;guest/join_success&quot;;
    }

    @RequestMapping(&quot;/login&quot;)
    public String login(Tbl_Member member, HttpSession session) {
        //1. 데이터 수집
        //2. 로직 실행
        Tbl_Member result = repo.findByEmailAndPw(member.getEmail(), member.getPw());

        if(result != null) {
            session.setAttribute(&quot;user&quot;, result);
        }

        //3. view 선택

        return &quot;redirect:/guest/main&quot;; //http:// ~~~/{contextPath}/ + &quot;redirect&quot;
    }

}</code></pre>
<p>@controller </p>
<ul>
<li>Spring Boot에게 Controller 라는 것을 알려주기 위해 클래스 바로 위에 작성</li>
</ul>
<p>@RequestMapper(&quot;/guest&quot;)</p>
<ul>
<li>이 컨트롤러에 접근하는 모든 Mapping은 &#39;/guest&#39;를 작성해야하기 때문에 클래스에서 한번에 작성한다.</li>
</ul>
<br>

<pre><code class="language-java">    //Repository 주입
    //Interface &gt; 추상메소드 밖에 없음 &gt;&gt; 객체 생성 불가
    @Autowired // Spring 메모리(Spring Container) 내에서 넣어줄 수 있는 객체를 알아서 찾아서 넣어달라
    private MemberRepository repo;</code></pre>
<p>DB와 연결하기 위한 인터페이스 객체를 미리 불러들인다.</p>
<p>이후 메모리 내에서 알아서 찾아서 넣어주는 @Autowired 를 위에 작성한다.</p>
<p>이렇게 해두면 나중에 DB에서 데이터를 가져올때</p>
<pre><code>Tbl_Member result = repo.findByEmailAndPw(member.getEmail(), member.getPw());</code></pre><p>이런식으로 사용할수 있다.</p>
<br>

<h3 id="메인화면-이동">메인화면 이동</h3>
<pre><code class="language-java">    @RequestMapping(&quot;/main&quot;)
    public String goMain() {

        return &quot;guest/main&quot;;
    }</code></pre>
<p>&quot;/guest/main&quot; 이라는 요청이 들어왔을 때 guest 폴더에 존재하는 main 페이지로 forword 된다.</p>
<h3 id="회원가입">회원가입</h3>
<pre><code class="language-java">    @RequestMapping(&quot;/join&quot;)
    public String join(Tbl_Member member) {

        repo.save(member);

        return &quot;guest/join_success&quot;;
    }</code></pre>
<p>위에 @RequestMapping(&quot;/join&quot;)이라 작성되어있지만 class 부분에서 @RequestMapping(&quot;/guest&quot;)를 선언했기 때문에 실제로 작동은 /guest/join을 해야 로그인 기능으로 들어갈 수 있다.</p>
<p>그리고 이번 실습은 jpa로 진행하기 때문에 repo라는 인터페이스에서 <strong>save()</strong> 메소드를 사용하여 수집한 정보를 DB에 저장한다.</p>
<br>

<h3 id="로그인">로그인</h3>
<pre><code class="language-java">    @RequestMapping(&quot;/login&quot;)
    public String login(Tbl_Member member, HttpSession session) {

        Tbl_Member result = repo.findByEmailAndPw(member.getEmail(), member.getPw());

        if(result != null) {
            session.setAttribute(&quot;user&quot;, result);
        }

        return &quot;redirect:/guest/main&quot;; </code></pre>
<p>이 mapping 은 매개변수로 Tbl_Member객체를 받아오고 계정을 session에 저장하기 위해 session 객체를 받아온다.</p>
<p>findByEmailAndPw()메소드를 통해 사용자가 입력한 email과 pw값을 이용하여 Tbl_Member 객체를 받는다.</p>
<p>만약 받아온 Tbl_Member 객체가 Null값이 아니라면 session에 &#39;user&#39; 이름으로 저장한다.</p>
<p>session에 저장했으면 다시 mainview로 이동해야하는데 이미 main으로 넘어가는 기능은 구현해놨기 때문에 redirect를 사용하여 해당 기능을 사용한다.</p>
<br>

<h4 id="일반적으로-이렇게-controller-dto-repository-이-3개를-한번에-생성해서-기능을-구현한다">일반적으로 이렇게 Controller, DTO, Repository 이 3개를 한번에 생성해서 기능을 구현한다.</h4>
<br>

<h2 id="main">Main</h2>
<hr>

<h4 id="전체코드-2">전체코드</h4>
<pre><code class="language-jsp">&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=UTF-8&quot; pageEncoding=&quot;UTF-8&quot;%&gt;  

&lt;!-- Context path 값을 받아올 수 있도록 --&gt;
&lt;%@ taglib uri=&quot;http://java.sun.com/jsp/jstl/core&quot; prefix=&quot;c&quot; %&gt;
&lt;% String cpath = request.getContextPath();
    pageContext.setAttribute(&quot;cpath&quot;, cpath);
    %&gt;
&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD HTML 4.01 Transitional//EN&quot; &quot;http://www.w3.org/TR/html4/loose.dtd&quot;&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Forty by HTML5 UP&lt;/title&gt;
        &lt;meta charset=&quot;UTF-8&quot; /&gt;
        &lt;!-- 
            href나 src 속성안에서 폴더 명으로 시작하는 경우
            &gt;&gt; http://localhost:8088/{ContextPath}/{mapping}/ + &quot;herf||src&quot;
            &gt;&gt; 잘못된 경로

            http://localhost:8088/{ContextPath} == static 파일을 지정함

            /로 시작하는 경우
            &gt;&gt; http://localhost:8088/ + &quot;href||src&quot;
         --&gt;
        &lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=UTF-8&quot;&gt;
        &lt;link rel=&quot;stylesheet&quot; href=&quot;${cpath}/assets/css/main.css&quot; /&gt;
    &lt;/head&gt;
    &lt;body&gt;

        &lt;!-- Wrapper --&gt;
            &lt;div id=&quot;wrapper&quot;&gt;

                &lt;!-- Header --&gt;
                    &lt;header id=&quot;header&quot; class=&quot;alt&quot;&gt;
                        &lt;a href=&quot;index.html&quot; class=&quot;logo&quot;&gt;&lt;strong&gt;Forty&lt;/strong&gt; &lt;span&gt;by HTML5 UP&lt;/span&gt;&lt;/a&gt;
                        &lt;nav&gt;
                            &lt;c:if test=&quot;${empty user}&quot;&gt;
                                &lt;a href=&quot;#menu&quot;&gt;로그인&lt;/a&gt;
                            &lt;/c:if&gt;
                            &lt;c:if test=&quot;${!empty user}&quot;&gt;
                                &lt;a href=&quot;${cpath}/member/list&quot;&gt;게시판&lt;/a&gt;
                                &lt;a href=&quot;#&quot;&gt;개인정보 수정&lt;/a&gt;
                                &lt;a href=&quot;#&quot;&gt;로그아웃&lt;/a&gt;
                            &lt;/c:if&gt;
                            &lt;!--Ex07. 로그인 한 상태라면 &#39;게시판&#39;버튼과 &#39;개인정보수정&#39;, &#39;로그아웃&#39; 버튼을 출력하시오. --&gt;
                        &lt;/nav&gt;
                    &lt;/header&gt;

                &lt;!-- Menu --&gt;
                    &lt;nav id=&quot;menu&quot;&gt;    
                        &lt;ul class=&quot;links&quot;&gt;
                            &lt;%--Ex07. 로그인 기능 만들기 : 사용자에게 정보를 입력받아 회원인지 아닌지 조회하는 기능을 만들어 봅시다. --%&gt;
                            &lt;li&gt;&lt;h5&gt;로그인&lt;/h5&gt;&lt;/li&gt;
                                &lt;form action = &quot;${cpath}/guest/login&quot; method=&quot;post&quot;&gt;
                                    &lt;li&gt;&lt;input name = &quot;email&quot; type=&quot;text&quot;  placeholder=&quot;Email을 입력하세요&quot;&gt;&lt;/li&gt;
                                    &lt;li&gt;&lt;input name = &quot;pw&quot; type=&quot;password&quot;  placeholder=&quot;PW를 입력하세요&quot;&gt;&lt;/li&gt;
                                    &lt;li&gt;&lt;input type=&quot;submit&quot; value=&quot;LogIn&quot; class=&quot;button fit&quot;&gt;&lt;/li&gt;
                                &lt;/form&gt;
                        &lt;/ul&gt;
                        &lt;ul class=&quot;actions vertical&quot;&gt;
                            &lt;%--Ex06. 회원가입 기능 만들기 : 사용자에게 정보를 입력받아 저장하는 기능을 만들어 봅시다. --%&gt;
                            &lt;li&gt;&lt;h5&gt;회원가입&lt;/h5&gt;&lt;/li&gt;
                            &lt;!-- boot/guest/join --&gt;
                                &lt;form action=&quot;${cpath}/guest/join&quot; method=&quot;post&quot;&gt;
                                    &lt;li&gt;&lt;input name=&quot;email&quot; type=&quot;text&quot;  placeholder=&quot;Email을 입력하세요&quot;&gt;&lt;/li&gt;
                                    &lt;li&gt;&lt;input name=&quot;pw&quot; type=&quot;password&quot;  placeholder=&quot;PW를 입력하세요&quot;&gt;&lt;/li&gt;
                                    &lt;li&gt;&lt;input name=&quot;tel&quot; type=&quot;text&quot;  placeholder=&quot;전화번호를 입력하세요&quot;&gt;&lt;/li&gt;
                                    &lt;li&gt;&lt;input name=&quot;address&quot; type=&quot;text&quot;  placeholder=&quot;집주소를 입력하세요&quot;&gt;&lt;/li&gt;
                                    &lt;li&gt;&lt;input type=&quot;submit&quot; value=&quot;JoinUs&quot; class=&quot;button fit&quot;&gt;&lt;/li&gt;
                                &lt;/form&gt;
                        &lt;/ul&gt;
                    &lt;/nav&gt;            
                &lt;!-- Banner --&gt;
                    &lt;section id=&quot;banner&quot; class=&quot;major&quot;&gt;
                        &lt;div class=&quot;inner&quot;&gt;
                            &lt;header class=&quot;major&quot;&gt;
                                &lt;%--Ex07. 로그인 후 로그인 한 사용자의 세션아이디로 바꾸시오. ex)smart님 환영합니다 --%&gt;
                                    &lt;h1&gt;로그인 한 세션아이디를 출력해주세요&lt;/h1&gt;
                            &lt;/header&gt;
                            &lt;div class=&quot;content&quot;&gt;
                                &lt;p&gt;아래는 지금까지 배운 웹 기술들입니다.&lt;br&gt;&lt;/p&gt;
                                &lt;ul class=&quot;actions&quot;&gt;
                                    &lt;li&gt;&lt;a href=&quot;#one&quot; class=&quot;button next scrolly&quot;&gt;확인하기&lt;/a&gt;&lt;/li&gt;
                                &lt;/ul&gt;
                            &lt;/div&gt;
                        &lt;/div&gt;
                    &lt;/section&gt;

                &lt;!-- Main --&gt;
                    &lt;div id=&quot;main&quot;&gt;

                        &lt;!-- One --&gt;
                            &lt;section id=&quot;one&quot; class=&quot;tiles&quot;&gt;
                                &lt;article&gt;
                                    &lt;span class=&quot;image&quot;&gt;
                                        &lt;img src=&quot;${cpath}/images/pic01.jpg&quot; alt=&quot;&quot; /&gt;
                                    &lt;/span&gt;
                                    &lt;header class=&quot;major&quot;&gt;
                                        &lt;h3&gt;&lt;a href=&quot;#&quot; class=&quot;link&quot;&gt;HTML&lt;/a&gt;&lt;/h3&gt;
                                        &lt;p&gt;홈페이지를 만드는 기초 언어&lt;/p&gt;
                                    &lt;/header&gt;
                                &lt;/article&gt;
                                &lt;article&gt;
                                    &lt;span class=&quot;image&quot;&gt;
                                        &lt;img src=&quot;${cpath}/images/pic02.jpg&quot; alt=&quot;&quot; /&gt;
                                    &lt;/span&gt;
                                    &lt;header class=&quot;major&quot;&gt;
                                        &lt;h3&gt;&lt;a href=&quot;#&quot; class=&quot;link&quot;&gt;CSS&lt;/a&gt;&lt;/h3&gt;
                                        &lt;p&gt;HTML을 디자인해주는 언어&lt;/p&gt;
                                    &lt;/header&gt;
                                &lt;/article&gt;
                                &lt;article&gt;
                                    &lt;span class=&quot;image&quot;&gt;
                                        &lt;img src=&quot;${cpath}/images/pic03.jpg&quot; alt=&quot;&quot; /&gt;
                                    &lt;/span&gt;
                                    &lt;header class=&quot;major&quot;&gt;
                                        &lt;h3&gt;&lt;a href=&quot;#&quot; class=&quot;link&quot;&gt;Servlet/JSP&lt;/a&gt;&lt;/h3&gt;
                                        &lt;p&gt;Java를 기본으로 한 웹 프로그래밍 언어/스크립트 언어&lt;/p&gt;
                                    &lt;/header&gt;
                                &lt;/article&gt;
                                &lt;article&gt;
                                    &lt;span class=&quot;image&quot;&gt;
                                        &lt;img src=&quot;${cpath}/images/pic04.jpg&quot; alt=&quot;&quot; /&gt;
                                    &lt;/span&gt;
                                    &lt;header class=&quot;major&quot;&gt;
                                        &lt;h3&gt;&lt;a href=&quot;#&quot; class=&quot;link&quot;&gt;JavaScript&lt;/a&gt;&lt;/h3&gt;
                                        &lt;p&gt;HTML에 기본적인 로직을 정의할 수 있는 언어&lt;/p&gt;
                                    &lt;/header&gt;
                                &lt;/article&gt;
                                &lt;article&gt;
                                    &lt;span class=&quot;image&quot;&gt;
                                        &lt;img src=&quot;${cpath}/images/pic05.jpg&quot; alt=&quot;&quot; /&gt;
                                    &lt;/span&gt;
                                    &lt;header class=&quot;major&quot;&gt;
                                        &lt;h3&gt;&lt;a href=&quot;#&quot; class=&quot;link&quot;&gt;MVC&lt;/a&gt;&lt;/h3&gt;
                                        &lt;p&gt;웹 프로젝트 중 가장 많이 사용하는 디자인패턴&lt;/p&gt;
                                    &lt;/header&gt;
                                &lt;/article&gt;
                                &lt;article&gt;
                                    &lt;span class=&quot;image&quot;&gt;
                                        &lt;img src=&quot;${cpath}/images/pic06.jpg&quot; alt=&quot;&quot; /&gt;
                                    &lt;/span&gt;
                                    &lt;header class=&quot;major&quot;&gt;
                                        &lt;h3&gt;&lt;a href=&quot;#&quot; class=&quot;link&quot;&gt;Web Project&lt;/a&gt;&lt;/h3&gt;
                                        &lt;p&gt;여러분의 최종프로젝트에 웹 기술을 활용하세요!&lt;/p&gt;
                                    &lt;/header&gt;
                                &lt;/article&gt;
                            &lt;/section&gt;
                    &lt;!-- Two --&gt;
                            &lt;section id=&quot;two&quot;&gt;
                                &lt;div class=&quot;inner&quot;&gt;
                                    &lt;header class=&quot;major&quot;&gt;
                                        &lt;h2&gt;메세지 확인하기&lt;/h2&gt;
                                    &lt;/header&gt;
                                    &lt;%-- chatting 기능을 만들어 봅시다! --%&gt;
                                    &lt;div class=&quot;container chat&quot;&gt;

                                        &lt;div class=&quot;other&quot;&gt;
                                            &lt;p&gt;보낸사람 이름 :&lt;/p&gt;
                                            &lt;p&gt;다른사람에게서 온 메세지&lt;/p&gt;
                                        &lt;/div&gt;

                                        &lt;div class=&quot;mychat&quot;&gt;
                                            &lt;p&gt;내가보낸 채팅&lt;/p&gt;
                                        &lt;/div&gt;

                                        &lt;div class=&quot;other&quot;&gt;
                                            &lt;p&gt;보낸사람 이름2 :&lt;/p&gt;
                                            &lt;p&gt;다른사람에게서 온 메세지2&lt;/p&gt;
                                        &lt;/div&gt;


                                    &lt;/div&gt;
                                    &lt;%-- 채팅창 끝! --%&gt;
                                &lt;/div&gt;
                            &lt;/section&gt;

                    &lt;/div&gt;

                &lt;!-- Contact --&gt;
                    &lt;section id=&quot;contact&quot;&gt;
                        &lt;div class=&quot;inner&quot;&gt;
                            &lt;section&gt;
                                &lt;form&gt;
                                    &lt;div class=&quot;field&quot;&gt;
                                        &lt;label for=&quot;message&quot;&gt;Message&lt;/label&gt;
                                        &lt;textarea  id=&quot;message&quot; rows=&quot;6&quot;&gt;&lt;/textarea&gt;
                                    &lt;/div&gt;
                                    &lt;ul class=&quot;actions&quot;&gt;
                                        &lt;li&gt;&lt;input type=&quot;button&quot; value=&quot;Send Message&quot; class=&quot;special&quot; /&gt;&lt;/li&gt;
                                        &lt;li&gt;&lt;input type=&quot;reset&quot; value=&quot;Clear&quot; /&gt;&lt;/li&gt;
                                    &lt;/ul&gt;
                                &lt;/form&gt;
                            &lt;/section&gt;

                            &lt;%--Ex07. 로그인 한 사용자의 정보로 변경해 봅시다. --%&gt;
                            &lt;section class=&quot;split&quot;&gt;
                                &lt;section&gt;
                                    &lt;div class=&quot;contact-method&quot;&gt;
                                        &lt;span class=&quot;icon alt fa-envelope&quot;&gt;&lt;/span&gt;
                                        &lt;h3&gt;Email&lt;/h3&gt;
                                        &lt;a href=&quot;#&quot;&gt;로그인 한 사람의 이메일을 출력&lt;/a&gt;
                                        &lt;!-- 로그인 한 사용자의 이메일을 출력하시오 --&gt;
                                    &lt;/div&gt;
                                &lt;/section&gt;
                                &lt;section&gt;
                                    &lt;div class=&quot;contact-method&quot;&gt;
                                        &lt;span class=&quot;icon alt fa-phone&quot;&gt;&lt;/span&gt;
                                        &lt;h3&gt;Phone&lt;/h3&gt;
                                        &lt;span&gt;로그인 한 사람의 전화번호를 출력&lt;/span&gt;
                                        &lt;!-- 로그인 한 사용자의 전화번호를 출력하시오 --&gt;
                                    &lt;/div&gt;
                                &lt;/section&gt;
                                &lt;section&gt;
                                    &lt;div class=&quot;contact-method&quot;&gt;
                                        &lt;span class=&quot;icon alt fa-home&quot;&gt;&lt;/span&gt;
                                        &lt;h3&gt;Address&lt;/h3&gt;
                                        &lt;span&gt;로그인 한 사람의 집주소를 출력&lt;/span&gt;
                                        &lt;!-- 로그인 한 사용자의 집주소를 출력하시오 --&gt;
                                    &lt;/div&gt;
                                &lt;/section&gt;
                            &lt;/section&gt;                    
                        &lt;/div&gt;
                    &lt;/section&gt;

                &lt;!-- Footer --&gt;
                    &lt;footer id=&quot;footer&quot;&gt;
                        &lt;div class=&quot;inner&quot;&gt;
                            &lt;ul class=&quot;icons&quot;&gt;
                                &lt;li&gt;&lt;a href=&quot;#&quot; class=&quot;icon alt fa-twitter&quot;&gt;&lt;span class=&quot;label&quot;&gt;Twitter&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
                                &lt;li&gt;&lt;a href=&quot;#&quot; class=&quot;icon alt fa-facebook&quot;&gt;&lt;span class=&quot;label&quot;&gt;Facebook&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
                                &lt;li&gt;&lt;a href=&quot;#&quot; class=&quot;icon alt fa-instagram&quot;&gt;&lt;span class=&quot;label&quot;&gt;Instagram&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
                                &lt;li&gt;&lt;a href=&quot;#&quot; class=&quot;icon alt fa-github&quot;&gt;&lt;span class=&quot;label&quot;&gt;GitHub&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
                                &lt;li&gt;&lt;a href=&quot;#&quot; class=&quot;icon alt fa-linkedin&quot;&gt;&lt;span class=&quot;label&quot;&gt;LinkedIn&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
                            &lt;/ul&gt;
                            &lt;ul class=&quot;copyright&quot;&gt;
                                &lt;li&gt;&amp;copy; Untitled&lt;/li&gt;&lt;li&gt;Design: &lt;a href=&quot;https://html5up.net&quot;&gt;HTML5 UP&lt;/a&gt;&lt;/li&gt;
                            &lt;/ul&gt;
                        &lt;/div&gt;
                    &lt;/footer&gt;

            &lt;/div&gt;

        &lt;!-- Scripts --&gt;
            &lt;script src=&quot;${cpath}/assets/js/jquery.min.js&quot;&gt;&lt;/script&gt;
            &lt;script src=&quot;${cpath}/assets/js/jquery.scrolly.min.js&quot;&gt;&lt;/script&gt;
            &lt;script src=&quot;${cpath}/assets/js/jquery.scrollex.min.js&quot;&gt;&lt;/script&gt;
            &lt;script src=&quot;${cpath}/assets/js/skel.min.js&quot;&gt;&lt;/script&gt;
            &lt;script src=&quot;${cpath}/assets/js/util.js&quot;&gt;&lt;/script&gt;
            &lt;!--[if lte IE 8]&gt;&lt;script src=&quot;${cpath}/assets/js/ie/respond.min.js&quot;&gt;&lt;/script&gt;&lt;![endif]--&gt;
            &lt;script src=&quot;${cpath}/assets/js/main.js&quot;&gt;&lt;/script&gt;

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

<h4 id="부분코드-1">부분코드 1</h4>
<pre><code class="language-jsp">&lt;%@ taglib uri=&quot;http://java.sun.com/jsp/jstl/core&quot; prefix=&quot;c&quot; %&gt;
&lt;% String cpath = request.getContextPath();
    pageContext.setAttribute(&quot;cpath&quot;, cpath);
    %&gt;</code></pre>
<p>jstl 을 사용하기위해 taglib을 가져오는 코드와 이후 jsp 링크에 편하게 설정하기위해 ContextPath를 cpath변수에 저장한다.</p>
<p>이후 page에서 사용할 수 있도록 pageContext에 setAttribute메소드를 사용하여 cpath를 저장한다.</p>
<br>

<h4 id="부분코드-2">부분코드 2</h4>
<pre><code class="language-jsp">&lt;c:if test=&quot;${empty user}&quot;&gt;
    &lt;a href=&quot;#menu&quot;&gt;로그인&lt;/a&gt;
&lt;/c:if&gt;
&lt;c:if test=&quot;${!empty user}&quot;&gt;
    &lt;a href=&quot;${cpath}/member/list&quot;&gt;게시판&lt;/a&gt;
    &lt;a href=&quot;#&quot;&gt;개인정보 수정&lt;/a&gt;
    &lt;a href=&quot;#&quot;&gt;로그아웃&lt;/a&gt;
&lt;/c:if&gt;</code></pre>
<p>jstl을 사용하여 if문을 사용하였으며, EL문법을 사용하여 로그인 상황을 확인한 뒤 이에 맞는 버튼을 출력한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Boot 환경설정]]></title>
            <link>https://velog.io/@jang_tissue/Spring-Boot</link>
            <guid>https://velog.io/@jang_tissue/Spring-Boot</guid>
            <pubDate>Thu, 11 Jan 2024 06:36:05 GMT</pubDate>
            <description><![CDATA[<h1 id="환경설정">환경설정</h1>
<hr>

<p>Spring boot 프로젝트 만들기</p>
<p><img src="https://velog.velcdn.com/images/jang_tissue/post/a5e3cfe4-749e-444e-97dd-ce67813f79ab/image.png" alt=""></p>
<ul>
<li>new &gt; other &gt; Spring &gt; Spring Starter Project</li>
</ul>
<p><img src="https://velog.velcdn.com/images/jang_tissue/post/28412b37-68a4-4246-b059-bcad49902bbf/image.png" alt=""></p>
<p>Service URL에 적힌 주소에 들어가서 개발환경을 온라인으로 설정할 수 있다.</p>
<ul>
<li>vscode나 intelli j 에서 spring boot를 사용하고자 한다면 꼭 온라인에서 환경 설정을 한 후 프로젝트를 import를 해서 프로젝트를 생성해야한다.</li>
</ul>
<p>Name : 프로젝트 이름
Artifact : Name과 동일
Type : 프로젝트 관리를 어떻게 할 것인가</p>
<ul>
<li>우리는 Maven 을 사용하기 때문에 Maven으로 클릭</li>
</ul>
<p>Package</p>
<ul>
<li>Jar : 독립적인 웹 어플리케이션 가능(Jsp 불가능)</li>
<li>War : Java 웹 페이지에 최적화 되어있음(Jsp불가능)</li>
</ul>
<p>Java Version : 17, 21 만 선택할 수 있는데 나중에 수동으로 바꿀 수 있음 &gt;&gt; pom.xml</p>
<p>Group : 생성하는 패키지 앞에 붙는 그룹 id</p>
<p>Package : 앞에서 설정한 Group까지만 설정하는 것을 권장함 &gt;&gt; 자동스캔때문</p>
<br>

<h4 id="starter">Starter</h4>
<ul>
<li>Springweb</li>
<li>lombok</li>
<li>Spring Boot DevTools(저장을 할 때 서버가 재 시작할 수 있도록 함)</li>
<li>thymeleaf &gt;&gt; html을 사용하고 싶을때 thymeleaf 설정</li>
</ul>
<br>

<p>다 만들었으면 pom.xml에 들어가서 자바 버전을 수정하면 됨</p>
<ul>
<li>parent 태그에 있는 version</li>
<li>properties에 있는 java.version</li>
</ul>
<p>이후 </p>
<ol>
<li>프로젝트 우클릭</li>
<li>Maven</li>
<li>project update</li>
</ol>
<p><img src="https://velog.velcdn.com/images/jang_tissue/post/e27f6337-f086-41d7-858e-8933edde8a7d/image.png" alt=""></p>
<h3 id="기존-spring-과-spring-boot가-다른점jar-기준">기존 Spring 과 Spring Boot가 다른점(jar 기준)</h3>
<ul>
<li>webApp 폴더가 존재하지 않음</li>
<li>xml 폴더가 없음 &gt;&gt; 더이상 xml파일을 만들지 않는다.</li>
<li>view 와 관련 파일들은 전부 src/main/resources 패키지 내부에 존재함!</li>
<li>tampleates : html 파일이 들어감</li>
<li>static : css, js, img, xml 등등이 들어감</li>
</ul>
<p>추가적으로 jsp를 사용하는 war 파일은 직접 파일을 추가해줘야함</p>
<ul>
<li>webapp &gt; WEB-INF/view</li>
<li>이 파일에 member와 guest 파일로 나눠서 파일생성</li>
<li>추가적으로 dependency 해줘야함</li>
</ul>
<pre><code class="language-java">       &lt;dependency&gt;
            &lt;groupId&gt;javax.servlet&lt;/groupId&gt;
            &lt;artifactId&gt;jstl&lt;/artifactId&gt;
            &lt;version&gt;1.2&lt;/version&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.apache.tomcat.embed&lt;/groupId&gt;
            &lt;artifactId&gt;tomcat-embed-jasper&lt;/artifactId&gt;
        &lt;/dependency&gt;</code></pre>
<br>

<h4 id="spring-boot-실행방법-3가지">Spring boot 실행방법 3가지</h4>
<ol>
<li>project클릭 &gt; Run as &gt; Spring Boot App</li>
<li>application.java 파일 &gt; main 메소드에 있는 파일 실행</li>
<li>window &gt; show view &gt; Boot Dashboard</li>
</ol>
<br>

<p>Spring boot는 내장 톰캣이 존재하기 때문에 외부 설정이 통하지 않는다.</p>
<p>그래서 우리가 Spring Boot에 관련된 설정을 진행할때는 전부 application.properties파일에서 설정해줄 수 있다.</p>
<p>application.properties을 파일을 열때는 그냥 열어서는 안된다(자동완성이 안됨)</p>
<ul>
<li>우클릭 &gt; open With &gt; Generic Edition</li>
</ul>
<pre><code class="language-java"># 내장 Tomcat 서버 port 번호 설정
server.port=8085

#http://localhost:8081/{ContextPath}/{URLMapping}
server.servlet.context-path=/boot</code></pre>
<p>위 코드는 내장 Tomcat 서버 port 번호를 재 설정 한 코드와 context path 설정한 것이다.</p>
<p>이렇게 기본적인 환경설정이 끝났다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스_가장 큰 수]]></title>
            <link>https://velog.io/@jang_tissue/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4%EA%B0%80%EC%9E%A5-%ED%81%B0-%EC%88%98</link>
            <guid>https://velog.io/@jang_tissue/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4%EA%B0%80%EC%9E%A5-%ED%81%B0-%EC%88%98</guid>
            <pubDate>Wed, 10 Jan 2024 11:12:10 GMT</pubDate>
            <description><![CDATA[<h2 id="문제설명">문제설명</h2>
<hr>

<p>0 또는 양의 정수가 주어졌을 때, 정수를 이어 붙여 만들 수 있는 가장 큰 수를 알아내 주세요.</p>
<p>예를 들어, 주어진 정수가 [6, 10, 2]라면 [6102, 6210, 1062, 1026, 2610, 2106]를 만들 수 있고, 이중 가장 큰 수는 6210입니다.</p>
<p>0 또는 양의 정수가 담긴 배열 numbers가 매개변수로 주어질 때, 순서를 재배치하여 만들 수 있는 가장 큰 수를 문자열로 바꾸어 return 하도록 solution 함수를 작성해주세요.</p>
<br>

<h2 id="제한사항">제한사항</h2>
<hr>

<ul>
<li>numbers의 길이는 1 이상 100,000 이하입니다.</li>
<li>numbers의 원소는 0 이상 1,000 이하입니다.</li>
<li>정답이 너무 클 수 있으니 문자열로 바꾸어 return 합니다.</li>
</ul>
<br>

<h2 id="입출력-예">입출력 예</h2>
<hr>

<p><img src="https://velog.velcdn.com/images/jang_tissue/post/df205b0f-fa1b-4a79-b21e-2813eaa61daf/image.png" alt=""></p>
<br>

<h2 id="코드">코드</h2>
<hr>

<pre><code class="language-java">import java.util.*;

class Solution {
    public String solution(int[] numbers) {
        String answer = &quot;&quot;;
        String[] arr = new String[numbers.length];
        for (int i = 0; i &lt; numbers.length; i++) {
            arr[i] = &quot;&quot; + numbers[i];
        }
        Arrays.sort(arr, new Comparator&lt;String&gt;() {
            public int compare(String o1, String o2) {

                return ((o2 + o1).compareTo(o1 + o2));
            }
        });
        if(arr[0].equals(&quot;0&quot;)) {
            return &quot;0&quot;;
        }

        for (String a : arr) {
            answer += a;
        }
        return answer;
    }
}</code></pre>
<br>

<h2 id="회고">회고</h2>
<hr>

<p>처음에 문제를 보고 와 이렇게 간단한 문제가 LV2라고? 라는 오만한 생각을 했다.</p>
<p>하지만 이 생각은 진짜 오만했다.</p>
<p>예제에 있는 것 처럼 3,30, 38, 302 가 있다면 숫자의 크기가 아니라 숫자 자릿수의 크기대로 정렬을 해야한다는 것이었다!</p>
<p>각 자릿수대로 ArrayList에 넣어서 비교를 한다던가, 2차원 배열에 넣어서 비교한다던가 여러가지 삽질을 해봤는데 결국 안됐다.</p>
<p>그래서 구글링을 진행한 결과 Comparator 이라는 인터페이스를 새롭게 알게됐다.</p>
<h3 id="comparator-인터페이스">Comparator 인터페이스</h3>
<p>Comparator은 두개의 값을 비교해서 정렬값을 내가 원하는 대로 정렬할 수 있게 해주는 것이다.</p>
<p>해당 인터페이스를 구현하는 클래스는 compratorTo(Object o) 메소드를 오버라이드 해야하고, 이 메소드는 객체 자신을 인수를 받은 객체와 비교한다.</p>
<p>위 문제에서 활용한 것은</p>
<p>정수형태의 문자열 2개를 연결 했을 때 더 큰 숫자를 먼저 정렬하도록 하는 것이다.</p>
<p>예를 들어 3, 34 일때 “34”+”3” = “343” 이 “3”+”34”=”334” 보다 크기 때문에 결과적으로 정렬값이 34, 3 으로 정렬된다.</p>
<p>이 것을 잘 활용하면 앞으로 문제 풀 때 더 수월하게 풀 수 있을 것 같다!!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스_기능개발]]></title>
            <link>https://velog.io/@jang_tissue/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4%EA%B8%B0%EB%8A%A5%EA%B0%9C%EB%B0%9C</link>
            <guid>https://velog.io/@jang_tissue/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4%EA%B8%B0%EB%8A%A5%EA%B0%9C%EB%B0%9C</guid>
            <pubDate>Tue, 09 Jan 2024 11:35:31 GMT</pubDate>
            <description><![CDATA[<h3 id="문제설명">문제설명</h3>
<hr>

<p>프로그래머스 팀에서는 기능 개선 작업을 수행 중입니다. 각 기능은 진도가 100%일 때 서비스에 반영할 수 있습니다.</p>
<p>또, 각 기능의 개발속도는 모두 다르기 때문에 뒤에 있는 기능이 앞에 있는 기능보다 먼저 개발될 수 있고, 이때 뒤에 있는 기능은 앞에 있는 기능이 배포될 때 함께 배포됩니다.</p>
<p>먼저 배포되어야 하는 순서대로 작업의 진도가 적힌 정수 배열 progresses와 각 작업의 개발 속도가 적힌 정수 배열 speeds가 주어질 때 각 배포마다 몇 개의 기능이 배포되는지를 return 하도록 solution 함수를 완성하세요.</p>
<br>


<h3 id="제한-사항">제한 사항</h3>
<hr>

<ul>
<li>작업의 개수(progresses, speeds배열의 길이)는 100개 이하입니다.</li>
<li>작업 진도는 100 미만의 자연수입니다.</li>
<li>작업 속도는 100 이하의 자연수입니다.</li>
<li>배포는 하루에 한 번만 할 수 있으며, 하루의 끝에 이루어진다고 가정합니다. 예를 들어 진도율이 95%인 작업의 개발 속도가 하루에 4%라면 배포는 2일 뒤에 이루어집니다.</li>
</ul>
<br>


<h3 id="입출력-예">입출력 예</h3>
<hr>

<p><img src="https://velog.velcdn.com/images/jang_tissue/post/eaacfe65-d80a-4da8-92cb-e5df0db4a4b2/image.png" alt=""></p>
<br>

<h3 id="코드">코드</h3>
<hr>

<pre><code class="language-java">import java.util.*;

class Solution {
    public ArrayList&lt;Integer&gt; solution(int[] progresses, int[] speeds) {

        //각 일마다 최소 일 수
        Queue&lt;Integer&gt; que = new LinkedList();

        //결과 List
        ArrayList&lt;Integer&gt; arr = new ArrayList();

        //해야하는 총 일 수 que에 저장
        for (int i = 0; i &lt; speeds.length; i++) {
            if ((100 - progresses[i]) % speeds[i] == 0) {
                //나머지가 존재한다면 100 초과가 되어야하기에 +1 
                que.add((100 - progresses[i]) / speeds[i]);
            } else {
                que.add((100 - progresses[i]) / speeds[i] + 1);
            }
        }

        // que에 첫 번째 일수가 저장됨
        int result = que.poll(); // que 첫 번째 값을 result에 저장

        //기본적으로 해야하는 일
        int a = 1;
        int size = que.size();
        for (int i = 0; i &lt; size; i++) {
            //만약 
            if (result &gt;= que.peek()) {
                a++;
                que.poll();
            } else {
                arr.add(a);
                result = que.poll();
                a = 1;
            }
        }

        //반복문 이후 남은 a 값 추가
        arr.add(a);
        return arr;
    }
}</code></pre>
<br>

<h3 id="설명">설명</h3>
<hr>

<p>처음에 문제를 보자마자 Queue를 사용해서 푸는 문제라 판단하기까지는 오래걸리지 않았다.</p>
<p>걸리는 총 일수를 계산하기를 que가 아니라 배열에 저장하려고 했는데, 코딩을 하다보니 불 필요한 것이라 판단되었다.</p>
<p>변수 a는 앞에 일이 끝나면 같이 끝나는 뒤에 일의 갯수이다.</p>
<p>그래서 queue에 처음부터 계산된 일수를 집어넣고,</p>
<p>앞 자리 숫자보다 작다면 한번에 끝나는 일의 변수 a의 값++ 하고 Queue의 앞 수를 Poll하여 삭제한다.</p>
<p>이와 반대로 앞 자리 숫자보다 크다면 그동안 쌓였던 a의 값을 arr에 넣어 저장 후 a의 값을 1로 초기화 한다.</p>
<p>여기서 함정이 있다.</p>
<p>마지막 3개의 수가 앞에 1개의 수보다 다 작다면 arr에 저장되지 않고 a에 그대로 쌓여있기만 한다.</p>
<p>그래서 마지막에 arr에 a의 값을 추가하여 return하면 마무리된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Boot 환경설정]]></title>
            <link>https://velog.io/@jang_tissue/Spring-Boot-%ED%99%98%EA%B2%BD%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@jang_tissue/Spring-Boot-%ED%99%98%EA%B2%BD%EC%84%A4%EC%A0%95</guid>
            <pubDate>Mon, 08 Jan 2024 08:31:19 GMT</pubDate>
            <description><![CDATA[<h1 id="spring-boot-프로젝트-만들기">Spring Boot 프로젝트 만들기</h1>
<hr>

<p>프로젝트 생성 &gt; other &gt; Spring starter Project</p>
<p><img src="https://velog.velcdn.com/images/jang_tissue/post/7cc7463c-c181-49af-a855-31a0ca2e0e51/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/jang_tissue/post/015ae0c1-db27-45ec-88f9-6b46f637f538/image.png" alt=""></p>
<p>맨위에 있는 Service url 주소에 들어가서 개발환경을 설정하고 해당 프로젝트를 import 받으면 똑같이 프로젝트 만들 수 있음</p>
<ul>
<li>vscode나 intelli J 에서 Spring boot 프로젝트를 만들고자 한다면 꼭 저기 들어가서 만들어야함</li>
<li>name : 프로젝트 이름</li>
<li>Artifact : Name과 동일</li>
<li>Type : 프로젝트 관리를 어떻게 할 것인지 (보통 Maven으로 선택)</li>
<li>Packaging : 배포할때 사용하는 것 Jar &gt;&gt; html 파일만으로 보여줄꺼면 클릭, War &gt;&gt; Jsp 파일도 인식 시키고 싶다면 이거 클릭</li>
<li>Java Version : 처음 선택할땐 17, 21만 있는데 나중에 낮출 수 있음</li>
<li>Group : 생성하는 패키지 앞에 붙는 그룹 id</li>
<li>Pakage : 앞에서 설정한 Group까지만 설정하는 것을 권장함(자동스캔때문에!!)</li>
</ul>
<h3 id="설정완료">설정완료</h3>
<p><img src="https://velog.velcdn.com/images/jang_tissue/post/89466fe0-13ed-48ab-aae0-62010fe58db0/image.png" alt=""></p>
<p>Spring boot 에서는 DB와 연동하기 위해서 다운받는 mybatis 라이브러리들을 한번에 다운받을 수 있게 함 &gt;&gt; starter</p>
<h4 id="starter">starter</h4>
<ul>
<li>Spring web</li>
<li>lombok</li>
<li>Spring Boot DevTools : 저장을 할때 서버가 자동으로 재 시작 할 수 있도록 함</li>
<li>thymeleaf</li>
</ul>
<br>

<h4 id="thymeleaf">thymeleaf</h4>
<ul>
<li>프로젝트에서 thml만 사용하고 싶다면 thymeleaf 를 설정해야한다.</li>
<li>그래야 viewResolve에서 .html로 설정해준다.</li>
<li>만약 JSP 파일도 설정하고싶다면 thymeleaf를 선택하지 말던가, 혹은 application.properties에서 설정을 해야한다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/jang_tissue/post/151d0da6-cc1f-4def-abf3-4fbecce82e3d/image.png" alt=""></p>
<br>

<h4 id="아까-설정-못한-버전-설정하는법">아까 설정 못한 버전 설정하는법</h4>
<ul>
<li>pom.xml에 들어가서 버전 수정</li>
</ul>
<ul>
<li>parent 태그에 있는 version : Spring boot</li>
<li>properties 에 있는 java.version : java</li>
</ul>
<br>

<h2 id="기본-spring과-spring-boot의-다른점">기본 Spring과 Spring Boot의 다른점</h2>
<hr>

<ul>
<li>webApp 폴더가 존재하지 않음</li>
<li>xml 폴더가 없음 &gt; 더이상 xml 파일을 만들지 않음</li>
<li>거의 view에 대한 내용이 들어감</li>
</ul>
<h3 id="view-관련-파일들은-전부-srcmainresources-패키지-내부에-존재함">view 관련 파일들은 전부 src/main/resources 패키지 내부에 존재함</h3>
<ul>
<li>tampleates : html파일이 들어감</li>
<li>static : css, js, img 파일 등 직접적으로 실행되지 않는 파일이 들어감</li>
</ul>
<br>

<h3 id="spring-boot-실행-방법-3가지">Spring Boot 실행 방법 3가지</h3>
<ol>
<li>project 클릭 &gt;&gt; Run as &gt;&gt; Spring Boot App</li>
<li>Application.java 파일 &gt;&gt; main 메소드 있는 파일 실행</li>
<li>window &gt;&gt; show view &gt;&gt; Boot Dashboard</li>
</ol>
<h4 id="spring-boot는-내장-톰캣이기-때문에-외부-설정이-통하지-않음">Spring boot는 내장 톰캣이기 때문에 외부 설정이 통하지 않음</h4>
<p>그래서 모든 파일의 설정은 application.properties 파일에서 해야함</p>
<br>

<h2 id="어떻게-webxml-파일이-존재하지-않는데-controller를-잘-찾아갈까">어떻게 Web.xml 파일이 존재하지 않는데 Controller를 잘 찾아갈까?</h2>
<hr>

<p>이에 대한 해답은 @SpringBootApplication 이라는 어노테이션 때문이라 할 수 있다.</p>
<p>이 어노테이션은 main 메소드가 존재하는 파일에 존재한다.
(참고로 main메소드는 프로그램이 시작될 때 가장 먼저 실행되는 메소드이다)</p>
<p>@SpringBootApplication 어노테이션은 @Autoconfigration과 @ComponentScan 이라는 어노테이션을 합친거다.</p>
<h4 id="autoconfigration">@Autoconfigration</h4>
<ul>
<li>SpringBoot 프로젝트를 실행하기 위해 필요한 설정들이 클래스로 정의되어있음</li>
<li>SpringBoot 프로젝트 시작시 준비해놨던 설정 클래스들을 전부 세팅 Ex)application.properties에 작성한 설정들</li>
<li>json 파일을 보고 일치하는 클래스에 설정들을 세팅 &gt;&gt; 프로젝트에 반영</li>
<li>라이브러리를 사용하기위해 필요한 기본(필수) 설정들을 자동으로 진행</li>
</ul>
<h4 id="componentscan">@ComponentScan</h4>
<ul>
<li>특정 패키지를 스캔해서 @Controller, @RestController 등 특별한 클래스를 찾아 Bean으로 생성하는 어노테이션</li>
<li>Application.java 파일과 같은 위치에 있는 모든 패키지를 자동으로 스캔</li>
<li>com.smhrd.~~</li>
<li>com/smhrd/controller</li>
<li>com.smhrd옆에 존재하는 모든 파일을 스캔함</li>
</ul>
<h4 id="즉-이러한-기능들-때문에-controller를-스캔하여-잘-찾아가게된다">즉, 이러한 기능들 때문에 Controller를 스캔하여 잘 찾아가게된다.</h4>
<br>

<h2 id="파일을-html-파일로-만들었는데-어떻게-정보를-전달할까">파일을 html 파일로 만들었는데 어떻게 정보를 전달할까?</h2>
<p>이 의문은 html로만 페이지를 만든다고 할때 부터 들었던 의문이다.</p>
<p>답은 간단했다.</p>
<p>thymeleaf 기능이 html 파싱을 하는 녀석이다.
그래서 html 파일에서 html 태그에 스키마를 해주면 된다.</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html xmlns:th=&quot;https://www.thymeleaf.org&quot;&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;Insert title here&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;HTML 메인 페이지&lt;/h1&gt;
    &lt;table&gt;
        &lt;!-- 
        &lt;tr th:each=&quot;&quot;&gt;
        --&gt;
        &lt;tr th:each=&quot;name : ${names}&quot;&gt;
            &lt;td th:text=&quot;${name}&quot;&gt;&lt;/td&gt;
        &lt;/tr&gt;
    &lt;/table&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<pre><code class="language-html">&lt;html xmlns:th=&quot;https://www.thymeleaf.org&quot;&gt;</code></pre>
<p>여기서 url을 통해 가져올 수 있다.</p>
<pre><code class="language-html">        &lt;tr th:each=&quot;name : ${names}&quot;&gt;
            &lt;td th:text=&quot;${name}&quot;&gt;&lt;/td&gt;
        &lt;/tr&gt;</code></pre>
<p>이 태그에서 jstl과 비슷하게 사용할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[올바른 괄호]]></title>
            <link>https://velog.io/@jang_tissue/%EC%98%AC%EB%B0%94%EB%A5%B8-%EA%B4%84%ED%98%B8</link>
            <guid>https://velog.io/@jang_tissue/%EC%98%AC%EB%B0%94%EB%A5%B8-%EA%B4%84%ED%98%B8</guid>
            <pubDate>Fri, 05 Jan 2024 09:06:34 GMT</pubDate>
            <description><![CDATA[<h2 id="문제설명-제한사항">문제설명, 제한사항</h2>
<hr>

<p><img src="https://velog.velcdn.com/images/jang_tissue/post/415b3914-9f3f-4036-b4fd-5b1a884f7519/image.png" alt=""></p>
<br>

<h2 id="입출력-예">입출력 예</h2>
<hr>

<p><img src="https://velog.velcdn.com/images/jang_tissue/post/6bcfe16e-57d5-4e01-a209-75444d56cdb7/image.png" alt=""></p>
<p>여기서 주의해야할점이 있다.
단순히 &#39;(&#39;와 &#39;)&#39;의 갯수가 맞아야만 하는것이 아니다.</p>
<p>무조건 먼저 &#39;(&#39;가 나와서 괄호를 열어야하고, 열린만큼 닫는기호가 있어야한다.(닫는 기호가 더 많아서도 안된다)</p>
<h4 id="처음-시도-코드">처음 시도 코드</h4>
<pre><code class="language-java">class Solution {
    boolean solution(String s) {
        int result = 0;
        char[] c = s.toCharArray();
        for(int i=0; i&lt;c.length; i++){
            if(c[i]==&#39;(&#39;)
                result++;
            else
                result--;
        }
        return result==0;
    }
}</code></pre>
<p>처음에 단순히 &#39;(&#39;, &#39;)&#39;의 갯수만 같아야하는 줄 알았다....</p>
<p>하지만 5번 11번 답이 자꾸 틀리길래 구글링 해서 알아본 결과
())(() 이 예외가 있다고 한다.</p>
<p>그래서 결국 result 값이 마지막에 0이면 되는 것이 아니라, 매 반복 회차마다 검사하여 0보다 작아지는 경우 return false;를 했더니 정답이었다.!</p>
<h2 id="최종-코드">최종 코드</h2>
<hr>

<pre><code class="language-java">class Solution {
    boolean solution(String s) {
        int result = 0;
        char[] c = s.toCharArray();
        for(int i=0; i&lt;c.length; i++){
            if(c[i]==&#39;(&#39;)
                result++;
            else
                result--;
            if(result&lt;0)
                return false;
        }
        return result==0;
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[퍼셉트론 개념]]></title>
            <link>https://velog.io/@jang_tissue/%ED%8D%BC%EC%85%89%ED%8A%B8%EB%A1%A0-%EA%B0%9C%EB%85%90</link>
            <guid>https://velog.io/@jang_tissue/%ED%8D%BC%EC%85%89%ED%8A%B8%EB%A1%A0-%EA%B0%9C%EB%85%90</guid>
            <pubDate>Thu, 04 Jan 2024 11:50:34 GMT</pubDate>
            <description><![CDATA[<h1 id="퍼셉트론">퍼셉트론</h1>
<hr>

<p>퍼셉트론이란??</p>
<ul>
<li>인공신경망 구성요소 중 하나로 딥러닝 모델의 가장 작은 기본단위</li>
<li>뇌를 구성하는 신경세포인 뉴런의 동작과 유사하게 동작</li>
<li>사람처럼 생각하기 위해 activation 함수를 뒤에 붙혀서 사용</li>
<li>역치 이상의 자극만 다음 뉴런에게 전달 &gt; 역치의 역할 = 활성화 함수
<img src="https://velog.velcdn.com/images/jang_tissue/post/ea5ad9a0-8e08-4b96-9413-3d88ce33eb11/image.png" alt="">
모든 자극을 그냥 전달할 수 없으니, 일정 기준에 따라 전달한다는 뜻이다.</li>
</ul>
<p>또한, 퍼셉트론 하나는 선형 함수 하나를 의미한다.</p>
<p>하지만 선형함수 하나만 존재할때 퍼셉트론이라 말할 수 없다.</p>
<p>선형 모델에 &#39;활성화 함수&#39;를 포함시켜야 퍼셉트론이라 할 수 있다.</p>
<h3 id="활성화함수">활성화함수</h3>
<hr>

<p>활성화 함수는 앞서 설명했듯 딥 러닝에서 꼭 필요한 존재이다.</p>
<p>앞서 말한 일정기준인 &#39;역치&#39;를 넘어서서 값을 전달한다는 뜻은 퍼셉트론이 &#39;계단함수&#39;를 사용한다는 뜻이다.</p>
<p>여기서 걸리는 것이 있다.
우리는 경사하강법을 사용하여 최적의 답을 구해야한다.
즉, 미분을 해야한다는 뜻이다.</p>
<p>하지만 계단함수는 미분이 불가능하여 경사하강법을 사용할 수 없다.</p>
<p>그래서 우리는 선형함수에 역치를 극복하기 위해 활성화 함수를 사용하는 것이다.</p>
<p><img src="https://velog.velcdn.com/images/jang_tissue/post/28117491-ac47-40f0-9cb9-3dcbb1ba079c/image.png" alt=""></p>
]]></description>
        </item>
    </channel>
</rss>