<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>jeong_uk.log</title>
        <link>https://velog.io/</link>
        <description>T자형 개발자가 되자</description>
        <lastBuildDate>Thu, 24 Apr 2025 10:37:25 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>jeong_uk.log</title>
            <url>https://velog.velcdn.com/images/jeong_uk/profile/ea42664d-bb09-4f54-b0ee-99c4f9763624/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. jeong_uk.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/jeong_uk" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[버블 정렬(Bubble Sort)]]></title>
            <link>https://velog.io/@jeong_uk/%EB%B2%84%EB%B8%94-%EC%A0%95%EB%A0%ACBubble-Sort</link>
            <guid>https://velog.io/@jeong_uk/%EB%B2%84%EB%B8%94-%EC%A0%95%EB%A0%ACBubble-Sort</guid>
            <pubDate>Thu, 24 Apr 2025 10:37:25 GMT</pubDate>
            <description><![CDATA[<h2 id="버블-정렬bubble-sort">버블 정렬(Bubble Sort)</h2>
<p>버블 정렬은 인접한 두 요소를 비교해서 큰 값을 뒤로 보내는 방식으로 작동합니다.
여러 번 반복하면서 가장 큰 값이 뒤로 가는 정렬입니다.</p>
<p>이제 코드로 구현해 보겠습니다. 코드로 구현 할 때의 핵심은 다로 for문을 어떻게 쓰냐인데요, 한번 살펴보도록 하겠습니다.</p>
<h4 id="📌-문제-버블-정렬">📌 문제 버블 정렬</h4>
<h4 id="설명">설명</h4>
<p>N개이 숫자가 입력되면 오름차순으로 정렬하여 출력하는 프로그램을 작성하세요.</p>
<p>정렬하는 방법은 버블정렬입니다.</p>
<h4 id="입력">입력</h4>
<p>첫 번째 줄에 자연수 N(1&lt;=N&lt;=100)이 주어집니다.</p>
<p>두 번째 줄에 N개의 자연수가 공백을 사이에 두고 입력됩니다. 각 자연수는 정수형 범위 안에 있습니다.</p>
<h4 id="출력">출력</h4>
<p>오름차순으로 정렬된 수열을 출력합니다.</p>
<h4 id="예시-입력-1">예시 입력 1</h4>
<p>6
13 5 11 7 23 15</p>
<h4 id="예시-출력-1">예시 출력 1</h4>
<p>5 7 11 13 15 23</p>
<h2 id="코드로-살펴보기">코드로 살펴보기</h2>
<pre><code>public class Main {
    public int[] solution(int n, int arr[]) {
        for(int i = 0; i &lt; n-1; i++) {

            for(int j = 0; j &lt; n-1-i; j++) {
                if(arr[j] &gt; arr[j+1]) {
                    int temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
        }

        return arr;
    }

    public static void main(String[] args) {
        Main T = new Main();
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int arr[] = new int[n];

        for(int i = 0; i &lt; n; i++) {
            arr[i] = sc.nextInt();
        }

        for(int i : T.solution(n, arr)) {
            System.out.print(i + &quot; &quot;);
        }
    }
}
</code></pre><h4 id="✔️-문제-풀이">✔️ 문제 풀이</h4>
<p>N개의 숫자가 입력되면, 이를 버블 정렬을 이용하여 오름차순으로 정렬한 뒤 출력하는 프로그램을 작성합니다.</p>
<p>버블 정렬은 인접한 두 수를 비교하여 왼쪽 수가 오른쪽 수보다 크면 자리를 바꾸는 방식입니다.
이 과정을 반복하면, 가장 큰 수가 점점 오른쪽 끝으로 이동하게 됩니다.</p>
<blockquote>
<p>바깥쪽 for문은 전체 배열을 몇 번 반복할지를 나타내며, 배열의 크기가 N이라면 최대 N-1번 반복하면 정렬이 완료됩니다.</p>
</blockquote>
<blockquote>
<p>안쪽 for문은 이웃한 숫자들끼리 비교하는 부분으로, 한 번 반복할 때마다 가장 큰 수 하나가 뒤로 확정되므로, N - 1 - i까지만 비교하면 됩니다.</p>
</blockquote>
<p>버블 정렬은 이중 for문으로 구성되므로, 전체 시간 복잡도는 O(n²) 입니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[선택 정렬]]></title>
            <link>https://velog.io/@jeong_uk/%EC%84%A0%ED%83%9D-%EC%A0%95%EB%A0%AC</link>
            <guid>https://velog.io/@jeong_uk/%EC%84%A0%ED%83%9D-%EC%A0%95%EB%A0%AC</guid>
            <pubDate>Wed, 23 Apr 2025 17:12:51 GMT</pubDate>
            <description><![CDATA[<p>선택 정렬은 매번 가장 큰값/가장 작은 값을 선택해서 정렬해주는 것을 말합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[후위식 연산(Postfix)]]></title>
            <link>https://velog.io/@jeong_uk/%ED%9B%84%EC%9C%84%EC%8B%9D-%EC%97%B0%EC%82%B0Postfix</link>
            <guid>https://velog.io/@jeong_uk/%ED%9B%84%EC%9C%84%EC%8B%9D-%EC%97%B0%EC%82%B0Postfix</guid>
            <pubDate>Sat, 12 Apr 2025 12:58:01 GMT</pubDate>
            <description><![CDATA[<p>✔️ 후위 표기식이란?
후위 표기식은 연산자 우선순위나 괄호 없이도
연산 순서가 정확이 결정되도록 만드는 표기법을 말합니다.</p>
<p>만약 3*(5+2)-9 을 후위연산식으로 표현하면 어떻게 되나요?</p>
<p>3 * (5 + 2) - 9 이라는 중위 표기식을 후위 표기식으로 바꿔보겠습니다.
✅ 중위 표기식:
3 * (5 + 2) - 9
🔄 변환 순서:
괄호 먼저 계산: (5 + 2) → 후위로 바꾸면 5 2 +
그 다음에 3 * (결과) → 3 5 2 + *
마지막으로 - 9 → 3 5 2 + * 9 -</p>
<p>✅ 결과: 후위 표기식 (Postfix)
3 5 2 + * 9 -
💡 후위 표기식의 특징:
괄호가 필요 없음
연산자의 우선순위는 위치로 결정
스택을 이용해서 쉽게 계산할 수 있음</p>
<p>🔄 후위표기 변환 규칙 (정리)
연산자 종류    후위 구조
a + b    a b +
a * (b + c)    a b c + *
a * b - c    a b * c -
a * (b + c) - d    a b c + * d -</p>
<p>💬 결론
연산자가 &quot;항상 자신의 피연산자 뒤에 온다&quot;는 말은,
자신이 적용되는 두 값(왼쪽과 오른쪽)이 먼저 나오고,
그 다음 연산자가 온다는 의미입니다.</p>
<h4 id="설명">설명</h4>
<p>후위연산식이 주어지면 연산한 결과를 출력하는 프로그램을 작성하세요.</p>
<p>만약 3<em>(5+2)-9 을 후위연산식으로 표현하면 352+</em>9- 로 표현되며 그 결과는 12입니다.</p>
<h4 id="입력">입력</h4>
<p>첫 줄에 후위연산식이 주어집니다. 연산식의 길이는 50을 넘지 않습니다.</p>
<p>식은 1~9의 숫자와 +, -, *, / 연산자로만 이루어진다.</p>
<h4 id="출력">출력</h4>
<p>연산한 결과를 출력합니다.</p>
<h4 id="예시-입력-1">예시 입력 1</h4>
<p>352+*9-</p>
<h4 id="예시-출력-1">예시 출력 1</h4>
<p>12</p>
<pre><code>public class Main {

    public int solution(String str) {
        int answer = 0;
        Stack&lt;Integer&gt; stack = new Stack&lt;&gt;();

        for(char c : str.toCharArray()) {
            if(Character.isDigit(c)) {
                stack.push(c - 48);

            } else {
                int rt = stack.pop();
                int lt = stack.pop();
                if(c ==&#39;+&#39;) {
                    stack.push(lt+rt);
                } else if(c==&#39;-&#39;) {
                    stack.push(lt-rt);
                } else if(c==&#39;*&#39;) {
                    stack.push(lt*rt);
                } else if(c==&#39;/&#39;) {
                    stack.push(lt/rt);
                }
            }
        }
        return stack.pop();
    }

    public static void main(String[] args) {
        Main T = new Main();
        Scanner sc = new Scanner(System.in);
        String str = sc.next();
        System.out.println(T.solution(str));
    }
}
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[프록시]]></title>
            <link>https://velog.io/@jeong_uk/%ED%94%84%EB%A1%9D%EC%8B%9C</link>
            <guid>https://velog.io/@jeong_uk/%ED%94%84%EB%A1%9D%EC%8B%9C</guid>
            <pubDate>Fri, 07 Mar 2025 10:59:43 GMT</pubDate>
            <description><![CDATA[<h4 id="📌-프록시proxy란">📌 프록시(Proxy)란?</h4>
<p>프록시는 대리 객체라고도 불린다. 실제 객체 대신 가짜 객체를 먼저 생성해서 필요할 때만 실제 객체에 접근하도록 도와주는 디자인 패턴이다. 
JPA에서는 데이터베이스 조회 성능 최적화를 위해서 프록시 객체를 사용한다.</p>
<h4 id="📌-jpa에서의-프록시-동작-방식">📌 JPA에서의 프록시 동작 방식</h4>
<pre><code>Member member = em.getReference(Member.class, memberId);</code></pre><p>이렇게 호출하면 JPA는 바로 데이터베이스에서 값을 조회하지 않고 프록시 객체만 반환한다.</p>
<h4 id="📌-프록시를-사용해야-하는-이유">📌 프록시를 사용해야 하는 이유</h4>
<p>✔️ 지연 로딩(Lazy Loading)</p>
<ul>
<li>엔티티의 연관된 데이터를 바로 조회하지 않고, 실제 접근할 때만 DB에 조회 쿼리를 실행한다.</li>
<li>즉, 성능 최적화와 메모리 절약 효과가 있다.
✔️ 최소한의 DB 조회
엔티티의 ID 값만 필요하다면 굳이 모든 필드를 조회하지 않고 프록시 객체만 반환한다.</li>
</ul>
<h4 id="📌-프록시의-내부-구조">📌 프록시의 내부 구조</h4>
<ul>
<li>식별자(ID)만 가지고 있으며, 나머지 필드는 가짜로 채워져 있다.</li>
<li>만약, 엔티티의 다른 필드 정보가 더 필요하다면 DB에서 데이터를 조회해서 실제 엔티티 객체를 초기화한다.</li>
<li>Hibernate는 이를 초기화 트리거 (Lazy Initialozation)라고 한다.<pre><code>Member member = em.getReference(Member.class, memberId);
System.out.println(member.getClass());
</code></pre></li>
</ul>
<p>// 식별자만 조회할 때는 초기화 X
System.out.println(member.getId()); // DB 조회 안함</p>
<p>// 다른 필드에 접근하면 DB 조회 발생 (프록시 초기화)
System.out.println(member.getUsername()); // DB 조회 발생</p>
<pre><code>![](https://velog.velcdn.com/images/jeong_uk/post/b526e848-70b3-4f99-83d4-5d2c59b12c53/image.png)

#### 프록시 초기화 과정
1. em.getReference() 호출 → 프록시 객체 반환
2. 엔티티의 식별자(ID) 외의 필드 접근 시 → DB 조회 발생 (초기화)
3. 실제 엔티티의 데이터가 프록시 객체의 target 필드에 저장
4. 프록시 객체는 여전히 프록시지만, 내부적으로 실제 엔티티의 데이터를 가진다.

#### 💡 중요한 점
- 초기화 전에는 프록시 객체가 식별자만 가지고 있다.
- 초기화 후에는 프록시 객체가 실제 엔티티 데이터를 target 필드에 보관하지만
타입은 프록시 객체의 타입 그대로 유지된다.

#### 🚨 주의사항
- 프록시 객체는 영속성 컨텍스트가 살아있을 때만 초기화가 가능하다.
- 영속성 컨텍스트가 종료된 상태에서 프록시 객체에 접근하면 LazyInitializationException이 발생한다.
- 프록시 객체는 원본 엔티티를 상속받음, 따라서 타입 체크시 주의해야함 (== 비
교 실패, 대신 instance of 사용)




</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[영속성 켄텍스트(Persistence Context)]]></title>
            <link>https://velog.io/@jeong_uk/%EC%98%81%EC%86%8D%EC%84%B1-%EC%BC%84%ED%85%8D%EC%8A%A4%ED%8A%B8Persistence-Context</link>
            <guid>https://velog.io/@jeong_uk/%EC%98%81%EC%86%8D%EC%84%B1-%EC%BC%84%ED%85%8D%EC%8A%A4%ED%8A%B8Persistence-Context</guid>
            <pubDate>Fri, 07 Feb 2025 14:56:07 GMT</pubDate>
            <description><![CDATA[<h3 id="영속성-컨텍스트란">영속성 컨텍스트란?</h3>
<ul>
<li>쉽게 말해서 엔티티를 영구 저장하는 환경이라고 생각하면 된다.</li>
<li>EntityManager.persist(entity);</li>
</ul>
<h3 id="영속성-컨텍스트의-이점">영속성 컨텍스트의 이점</h3>
<ol>
<li>1차 캐시</li>
<li>동일성(identity) 보장</li>
<li>트랙잭션을 지원하는 쓰기 지연 (transactional write-behind)</li>
<li>변경 감지 (Dirty Checking)</li>
<li>지연 로딩(Lazy Loading)</li>
</ol>
<p><img src="https://velog.velcdn.com/images/jeong_uk/post/c5c69423-623c-4864-aadf-986c1fb7eba7/image.png" alt="">
엔티티를 조회할 때, em.find()를 통해서 조회하게 되는데 위에 사진 처럼 1차 캐시에 없으면 DB에서 조회하여 1차 캐시에 저장하게 된다.</p>
<p>영속성 컨텍스트에 대해서 공부하다가 em.persist(entity)는 데이터베이스에 저장되는게 아니라 영속성 컨텍스트에 반영된다는게 이해가 잘 되지 않아서 코드를 통해서 이해하는게 더 빨랐다.</p>
<pre><code>EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();

tx.begin();  // 트랜잭션 시작

Member member = new Member();
member.setName(&quot;이순신&quot;);

em.persist(member);  // 영속성 컨텍스트에 등록 (SQL 실행 X)

tx.commit();  // 여기서 flush()가 발생하고, INSERT SQL 실행</code></pre><p>✅ persist() → 영속성 컨텍스트에 저장됨 (DB 반영 X)
✅ commit() → flush() 후 DB에 반영됨
✅ flush() → 변경 내용을 DB에 즉시 반영하지만, 트랜잭션은 유지됨</p>
<p>즉, persist()는 데이터베이스가 아니라 영속성 컨텍스트에 먼저 반영되는 것을 확인할 수 있다! 🚀</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[다양한 의존관계 주입 방법]]></title>
            <link>https://velog.io/@jeong_uk/%EB%8B%A4%EC%96%91%ED%95%9C-%EC%9D%98%EC%A1%B4%EA%B4%80%EA%B3%84-%EC%A3%BC%EC%9E%85-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@jeong_uk/%EB%8B%A4%EC%96%91%ED%95%9C-%EC%9D%98%EC%A1%B4%EA%B4%80%EA%B3%84-%EC%A3%BC%EC%9E%85-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Wed, 08 Jan 2025 13:32:44 GMT</pubDate>
            <description><![CDATA[<h1 id="다양한-의존관계-주입-방법">다양한 의존관계 주입 방법</h1>
<p>의존 관계 주입 방법으로 크게 4가지 방법이 있다.</p>
<ol>
<li>생성자 주입</li>
<li>수정자 주입(setter 주입)</li>
<li>필드 주입</li>
<li>일반 메서드 주입</li>
</ol>
<h4 id="생성자-주입">생성자 주입</h4>
<p>▪️생성자 주입은 생성자를 통해서 의존 관계를 주입 받는 방법을 말한다.
▪️생성자 호출 시점에 딱 1번만 호출히 가능하다.
▪️불변, 필수 의존관계에 사용된다.</p>
<pre><code>@Autowired
public OrderService(MemberRepository memberRepository, MemberService, memberService) {
 this.memberRepository = memberRepository;
 this.memberService = memberService;
    }
 }</code></pre><p>생성자가 1개만 있으면 @Autowired는 생략해도 자동으로 주입된다.</p>
<h4 id="수정자-주입setter-주입">수정자 주입(setter 주입)</h4>
<p>▪️setter라 불리는 필드의 값을 변경하는 수정자 메서드를 통해서 의존관계를 주입하는 방식이다.
▪️setter메서드를 통해서 선택, 변경 가능성이 있는 의존관계에 사용된다.</p>
<pre><code>@Autowired
public void setMemberRepository(MemberRepository memberRepository){
    this.memberRepository = memberRepository;
}</code></pre><p>setXxx, getXxx라는 메서드를 통해서 값을 읽거나 수정하는 규칙을 만들었는데, 이것이 자바빈 프로퍼티 규약이다.</p>
<h4 id="필드-주입">필드 주입</h4>
<p>▪️필드에 바로 주입하는 방식이다.
▪️코드가 간결하지만 치명적인 단점으로 외부에서 변경이 불가능해서 테스트하기하 어렵다.
▪️ DI 프레임워크가 없으면 아무것도 할 수 없다.
▪️사용하지 않는 것이 좋다.</p>
<pre><code>@Autowired
private MemberRepository memberRepository</code></pre><h4 id="일반-메서드-주입">일반 메서드 주입</h4>
<p>▪️일반 메서드를 통해서 주입 받을 수 있다.
▪️한번에 여러 필드를 주입 받을 수 있다.
▪️잘 사용하지 않는다.</p>
<pre><code>@Autowired
public void init(MemberRepository memberRepository, MemberService memberService){
    this.memberRepository = memberRepository;
    this.memberService = memberService;
}</code></pre><h4 id="autowired-옵션-처리">@Autowired 옵션 처리</h4>
<p>주입할 스프링 빈이 없어도 동작해야 할 때가 있다.
그런게 @Autowired만 사용하면 required 옵션의 기본값이 true로 되어 있어서 자동 주입 대상이 없으면 오류가 발생한다.</p>
<p>▪️저동 주입 대상을 옵션으로 처리하는 방법</p>
<pre><code>@Autowired(required = false)</code></pre><p>자동 주입할 대상이 없으면 수정자 메서드 자체가 호출이 안된다.</p>
<h1 id="최신-트랜드의-의존관계-주입">최신 트랜드의 의존관계 주입</h1>
<p>바로 @RequiredArgsConstructor 어노테이션을 사용하는 것이다.
▪️롬북 라이브러리가 제공하는 @RequiredArgsConstructor기능을 사용하면 final이 붙은 필드를 모아서 생성자를 자동으로 만들어준다.</p>
<pre><code>@RequiredArgsConstructor
public class OrderService {
    private final MemberRepository memberRepository
}</code></pre><p>정리하면 최근에는 생성자를 딱 1개 두고, @Autowired를 생략하는 방법을 주로 사용한다. 여기에 Lombok 라이브러리의 @RequiredArgsConstructor함께 사용하면 기능은 다 제공하면서, 코드는 깔끔해진다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[API를 이용한 문자인증]]></title>
            <link>https://velog.io/@jeong_uk/API%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EB%AC%B8%EC%9E%90%EC%9D%B8%EC%A6%9D</link>
            <guid>https://velog.io/@jeong_uk/API%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EB%AC%B8%EC%9E%90%EC%9D%B8%EC%A6%9D</guid>
            <pubDate>Sun, 24 Nov 2024 20:02:22 GMT</pubDate>
            <description><![CDATA[<p>이번 글에서는 CoolSMS와 Redis를 이용하여 인증번호가 담긴 문자 메시지를 발송하고, 인증번호를 검증하는 방법에 대해 자세히 설명드리겠습니다. 사실 이 주제를 발표 주제로 생각한 것은 아니었지만, API와 관련된 부분을 더 깊이 알고 싶다는 마음에 선택하게 되었습니다. CoolSMS와 Redis를 사용하여 문자 인증 기능을 구현하는 과정에서 많은 것들을 배우게 되었습니다.</p>
<h2 id="coolsms와-redis-소개">CoolSMS와 Redis 소개</h2>
<p>이번 문자 인증 기능 구현에 사용할 주요 기술은 CoolSMS와 Redis입니다.</p>
<p>CoolSMS는 카카오 알림톡 및 문자 메시지 발송을 지원하는 REST API 서비스입니다. 이 API를 사용한 이유는 관련 자료가 많아 참고하기 쉽고, 기본적으로 15회 정도 무료로 문자 메시지를 보낼 수 있다는 점이 매우 유용했기 때문입니다.</p>
<p>Redis는 인메모리 기반의 Key-Value NoSQL 데이터베이스로, 데이터를 메모리에 저장하기 때문에 읽기와 쓰기 속도가 매우 빠릅니다. 인증번호와 같은 휘발성 데이터를 저장하기에 적합하며, 데이터의 TTL(Time To Live)을 설정하여 일정 시간 후에 자동으로 삭제할 수 있어 인증번호 관리에 매우 용이합니다.</p>
<p>인증번호는 특성상 오랜 시간 저장할 필요가 없기 때문에, 이러한 휘발성 데이터를 관리하기 위해 Redis를 사용하게 되었습니다. RDMS를 사용할 경우 비효율적이고 복잡한 과정이 필요할 수 있으나, Redis는 간단하고 빠르게 인증번호를 처리할 수 있습니다.</p>
<h3 id="문자-인증-api">문자 인증 api</h3>
<p>문자 인증 기능은 CoolSMS와 Redis를 이용해 구현됩니다. CoolSMS를 통해 인증번호가 담긴 문자 메시지를 발송하고, Redis를 사용하여 인증번호를 저장 및 검증하는 방식입니다.</p>
<h4 id="문자-인증-api-개요">문자 인증 API 개요</h4>
<p>문자 인증 API의 주요 기능은 다음과 같습니다:</p>
<ol>
<li>인증번호 전송: 사용자가 입력한 전화번호로 인증번호를 전송합니다.</li>
<li>인증번호 검증: 사용자가 입력한 인증번호가 Redis에 저장된 번호와 일치하는지 확인합니다.</li>
</ol>
<h4 id="authservice-설명">AuthService 설명</h4>
<p>AuthService 클래스는 CoolSMS와 Redis를 이용하여 문자 인증을 구현합니다. 주요 기능은 다음과 같습니다:</p>
<p>인증번호 생성 및 전송: sendAuthCode() 메서드는 4자리 랜덤 인증번호를 생성하고, 이를 CoolSMS를 통해 사용자의 전화번호로 전송합니다. 이후 Redis에 이 인증번호를 저장하며, 유효기간은 5분으로 설정됩니다. 이는 인증번호가 일정 시간 이후 자동으로 만료되도록 하는데, Redis의 TTL 기능을 활용한 것입니다.</p>
<p>인증번호 검증: validateAuthCode() 메서드는 Redis에 저장된 인증번호와 사용자가 입력한 인증번호를 비교하여 인증 여부를 확인합니다.</p>
<pre><code>@Service
@RequiredArgsConstructor
public class AuthService {

    private final CoolSmsService coolSmsService; // SMS 전송 서비스
    private final RedisTemplate&lt;String, String&gt; redisTemplate; // Redis 템플릿

    public String sendAuthCode(String phoneNumber) throws Exception {
        String authCode = generateRandomNumber(); // 랜덤 인증코드 생성
        String message = &quot;인증번호는 [&quot; + authCode + &quot;] 입니다.&quot;; // SMS 내용

        // SMS 전송
        coolSmsService.sendSms(phoneNumber, message);

        // Redis에 인증번호 저장 (유효 기간 5분)
        redisTemplate.opsForValue().set(phoneNumber, authCode, 5, TimeUnit.MINUTES);

        return authCode; // 인증 코드 반환
    }

    public boolean validateAuthCode(String phoneNumber, String authCode) {
            // redis에서 저장된 인증코드 조회
        String storedCode = redisTemplate.opsForValue().get(phoneNumber); // redis의 데이터 조회
        // 저장된 코드와 입력된 코드 비교
        return storedCode != null &amp;&amp; storedCode.equals(authCode);
    }

    private String generateRandomNumber() {
        Random rand = new Random();
        StringBuilder numStr = new StringBuilder();
        for (int i = 0; i &lt; 4; i++) { // 4자리 인증번호
            numStr.append(rand.nextInt(10));
        }
        return numStr.toString(); // 생성된 인증번호 문자열로 반환
    }
}</code></pre><p><img src="https://velog.velcdn.com/images/jeong_uk/post/4cebafe4-5205-4923-bedc-c75d36b82ac7/image.png" alt="">
인증번호 전송 화면: 사용자가 전화번호를 입력하고 인증번호를 요청하면, 해당 번호로 인증번호가 전송됩니다.
<img src="https://velog.velcdn.com/images/jeong_uk/post/94827c5e-4315-4fca-b076-95f377849178/image.png" alt="">인증번호 전송 화면: 사용자가 전화번호를 입력하고 인증번호를 요청하면, 해당 번호로 인증번호가 전송됩니다.</p>
<h4 id="정리">정리</h4>
<p>CoolSMS와 Redis를 이용하여 문자 인증 기능을 구현하는 과정을 살펴보았습니다. CoolSMS를 통해 쉽게 문자 메시지를 발송하고, Redis의 인메모리 데이터베이스를 활용하여 빠르게 인증번호를 관리함으로써 효율적인 문자 인증 시스템을 구축할 수 있었습니다.</p>
<p>Redis의 인메모리 특성과 TTL 기능은 인증번호와 같은 휘발성 데이터를 관리하는 데 매우 유용하며, 이를 통해 인증번호의 생성, 전송, 검증을 효과적으로 처리할 수 있었습니다. 이번 과정을 통해 REST API 기반의 문자 인증 기능을 손쉽게 구축하는 방법을 배울 수 있었으며, 실무에서도 쉽게 응용할 수 있는 내용이었습니다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[AWS와 아마존의 관계 및 AWS기능]]></title>
            <link>https://velog.io/@jeong_uk/AWS%EC%99%80-%EC%95%84%EB%A7%88%EC%A1%B4%EC%9D%98-%EA%B4%80%EA%B3%84-%EB%B0%8F-AWS%EA%B8%B0%EB%8A%A5</link>
            <guid>https://velog.io/@jeong_uk/AWS%EC%99%80-%EC%95%84%EB%A7%88%EC%A1%B4%EC%9D%98-%EA%B4%80%EA%B3%84-%EB%B0%8F-AWS%EA%B8%B0%EB%8A%A5</guid>
            <pubDate>Sat, 26 Oct 2024 09:54:05 GMT</pubDate>
            <description><![CDATA[<h3 id="awsamazon-web-services와-아마존의-관계">AWS(Amazon Web Services)와 아마존의 관계</h3>
<p>AWS(Amazon Web Services)는 아마존(Amazon)의 클라우드 컴퓨팅 사업부입니다.
아마존의 내부 인프라를 관리하는 과정에서 발전시킨 클라우드 기술을 바탕으로 AWS를 론칭하게 됩니다. </p>
<p>AWS는 다양한 클라우드 기반의 제품과 서비스를 제공합니다. 이러한 서비스에는 컴퓨팅 파워, 데이터베이스 스토리지, 콘텐츠 전송 및 기타 기능이 포함됩니다. AWS를 사용함으로써 기업들은 물리적인 서버 구축 없이도 필요한 IT 인프라를 구축하고 운영할 수 있습니다.</p>
<p>왜냐하면 AWS는 유연성, 확장성, 비용 효율성을 제공하기 때문입니다. 기업들은 자신의 필요에 맞게 리소스를 쉽게 확장하거나 축소할 수 있으며, 사용한 만큼만 비용을 지불하게 됩니다.</p>
<h3 id="aws의-기능">AWS의 기능</h3>
<p>📌 컴퓨팅</p>
<ol>
<li>EC2(Elastic Compute Cloud)
Amazon Elastic Compute Cloud(EC2)는 탄력적인 컴퓨팅 서비스이다
빠르고 쉽게 원하는 사양의 서버를 구축,변경,삭제,추가등이 가능하다</li>
</ol>
<ol start="2">
<li>Lambda
 AWS Lambda는 이벤트에 대한 응답으로 코드를 실행하고 자동으로 기본 컴퓨팅 리소스를 관리하는 서비스이다.
 AWS Lambda를 사용하면 서버를 프로비저닝하거나 관리할 필요 없이 코드를 실행할 수 있다.</li>
</ol>
<p>📌 스토리지</p>
<p>클라우드 서비스의 스토리지는 기본적으로 높은 안정성 과 확장성을 가지고 있다.
AWS의 스토리지는 여러곳에 분산되어 저장되기 떄문에 자연재해나 화재에 대해 비교적 안전하다.</p>
<p>📌 모니터링</p>
<p>기본 모니터링과 CloudWatch같은 세부 모니터링을 제공한다.</p>
<ol>
<li><p>S3 (Simple Storage Services)
여러가지 용도로 사용이 가능한 범용 스토리지 서비스이다.
데이터 보관,정적인 웹 호스팅등등 다양한 형태의 서비스를 할 수 있다.</p>
</li>
<li><p>EBS(Elastic Block Storage)
탈부착이 쉽고 기본적으로 기본적으로 SSD를 지원하기떄문에 속도가 빠르다.
예를들어 A인스턴스에 적용한 EBS를 클릭 몇번으로 해제후 안에 들어있는 데이터를 손상없이 그대로 B인스턴스에 적용이 가능하다.</p>
</li>
<li><p>Glacier
가격이 저렴하다 다만 성능면에서는 다른 스토리지 서비스보다 떨어진다.
데이터 백업파일 로그기록등등 자주 사용하지 않는 데이터를 보관하기 좋다.</p>
</li>
</ol>
<p>📌 데이터 베이스</p>
<p>AWS의 데이터베이스는 Scaling, Backup, DB Application Path, DB Software linstalls, OS Path,
Server maintenance, Stack Power, Network를 관리해주기 때문에에만 집중할 수 있다.</p>
<ol>
<li>RDS
MySQL,Microsoft SQL,Oracle,Aurora,MariaDB등 데이터베이스 엔진을 제공한다.</li>
</ol>
<p>📌 네트워킹</p>
<ol>
<li>Amazon Route 53
DNS를 관리하거나 생성이 가능하다</li>
<li>VPC(Virtual Private Cloud)
가상의 사설 네트워크를 구성할 수 있게 만들어주는 기능이다.
네트워크의 접근을 제어할 수 있다(보안 그룹 , 네트워크 ACL), VPN연결 인터넷 게이트웨이 연결</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[Java - 가비지 컬렉션]]></title>
            <link>https://velog.io/@jeong_uk/Java-%EA%B0%80%EB%B9%84%EC%A7%80-%EC%BB%AC%EB%A0%89</link>
            <guid>https://velog.io/@jeong_uk/Java-%EA%B0%80%EB%B9%84%EC%A7%80-%EC%BB%AC%EB%A0%89</guid>
            <pubDate>Mon, 21 Oct 2024 07:44:30 GMT</pubDate>
            <description><![CDATA[<ul>
<li><p>자바에서는 불필요한 메모리 할당을 제거하기 위해 가비지 컬렉션이라는 기능을 제공한다. JVM의 Heap 영역에 동적으로 할당된 메모리 중에서 사용하지 않는 부분을 주기적으로 회수하는 작업이다. . 자바에서는 JVM에 탑재된 가비지 컬렉터가 알아서 해준다. 그렇다고 메모리에 대해 전혀 신경을 쓰지 않아도 되는 것은 아니니 필요한 상황에는 생성된 객체를 제거하거나 메모리 할당을 해제 해주어야 하는 경우도 종종있다. </p>
</li>
<li><p>그런데 자바의 이런 편리한 기능에도 장점만 있는 것은 아닌데, 가비지 컬렉션이 개발자가 컨트롤 하는 것이 아니기 때문에 언제 발생할 지 알 수 없다. 언제 발생할지 알 수 없다는 것은 프로그램의 특징에 따라서 치명적인 문제가 될 수도 있다. 왜냐면 가비지 컬렉션이 일어날 때 JVM의 다른 모든 동작들이 멈추기 때문이다. 예를 들어서 실시간으로 계속 프로세싱이 필요한 프로그램인데 중간에 가비지 컬렉션이 일어나게 되면 그 찰나의 데이터들을 놓치게 됨으로써 예상된 결과를 보지 못하거나 디버깅이 어려워질 수도 있다. 또 가비지 컬렉션은 다른 동작을 멈춘다는 오버헤드가 있기 때문에 너무 자주 발생하게 되면 성능에 문제가 될 수도 있다. </p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/jeong_uk/post/f2aa162f-0f97-4a5a-8023-124bf4bb5193/image.png" alt=""></p>
<ul>
<li>가비지 컬렉션은 Heap 영역에 생성된 객체들을 수거하는 것인데 아무 객체나 랜덤으로 수거하지는 않는다. 위의 그림에서 나타나듯이 Method 영역, Stack 영역에서 참조하고 있는 객체들은 제외 대상이고 참조되고 있지 않는 대상들이 수거가 된다. </li>
</ul>
<p><img src="https://velog.velcdn.com/images/jeong_uk/post/51e5ad1a-8fa2-49d5-bb3a-f76c8215d8e3/image.png" alt=""></p>
<ul>
<li><p>Heap 영역은 조금 더 효율적인 운영을 위해서 위와 같이 구조가 분리되어 있다. new 키워드를 이용해서 객체를 생성하면 Eden 영역에 객체가 생성이 된다. 그 후에 Eden 영역이 가득 차게되면 Minor 가비지 컬렉션이 일어나고 살아남을 때마다 age가 올라가게 되고 특정 age가 넘으면 survival 영역에서 old generation 영역으로 이동하게 된다. 그러다가 old generation 부분까지 가득 차게 되면 Major 가비지 컬렉션이 실행되게 된다. 이 때 old generation 영역의 객체 중에 참조되고 있지 않은 객체들을 회수가 된다. 이 때 JVM의 모든 동작이 멈추는데 이를 Stop The World 부른다. </p>
</li>
<li><p>가비지 컬렉션은 생각보다 간단한 프로세스로 진행된다. 실제로 내부적으로는 가비지 컬렉션 하는 알고리즘도 존재하는데 등은 버전에 따라 조금씩 다른 것으로 알고 있다. 더 궁금한 내용이 있다면 구글링을 추천한다. </p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[생성자 주입 방식을 사용하는 이유]]></title>
            <link>https://velog.io/@jeong_uk/dd-woua17uw</link>
            <guid>https://velog.io/@jeong_uk/dd-woua17uw</guid>
            <pubDate>Sun, 08 Sep 2024 07:25:37 GMT</pubDate>
            <description><![CDATA[<p>📌 의존관계 주입에는 크게 3가지 방법이 있습니다.</p>
<ul>
<li>생성자 주입</li>
<li>수정자 주입 (setter 주입)</li>
<li>필드 주입</li>
<li>일반 메서드 주입</li>
</ul>
<p>✔️ 생성자 주입 방식
생성자 주입 방식은 생성자를 통해서 의존관계를 주입 받는 방식입니다.
생성자 방식의 특징은 생성자 호출 시점에 딱 1번만 호출되는 것이 보장되기 때문에
불변객체를 생성하는데 유리합니다.</p>
<pre><code>@Component
public class OrderServiceImpl implements OrderService {
    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;

 @Autowired // 생성자 주입 방식
 public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy
discountPolicy) {
    this.memberRepository = memberRepository;
    this.discountPolicy = discountPolicy;
    }
}</code></pre><p>✔️ 수정자 주입 방식
수정자 주입 방식은 setter라 불리는 필드의 값을 변경하는 수정자 메서드를 통해서 의존관계를 주입하는 방법입니다. 주로 변경 가능이 있는 의존관계를 주입하는데 사용됩니다.</p>
<pre><code>@Component
public class OrderServiceImpl implements OrderService {
    private MemberRepository memberRepository;
    private DiscountPolicy discountPolicy;

 @Autowired // 수정자 주입 방식
 public void setMemberRepository(MemberRepository memberRepository) {
    this.memberRepository = memberRepository;
 }

 @Autowired // 수정자 주입 방식
 public void setDiscountPolicy(DiscountPolicy discountPolicy) {
    this.discountPolicy = discountPolicy;
    }
}</code></pre><p>✔️ 필드 주입
필드 주입 방식은 코드가 간결해서 좋아보이지만, 외부에서 변경이 불가능해서 테스트하기 힘들다는 단점이 있습니다. 필드 주입 방식은 이러한 이유 때문에 사용하지 않는 것이 좋습니다.</p>
<pre><code>@Component
public class OrderServiceImpl implements OrderService {
    @Autowired // 필드 주입 방식
    private MemberRepository memberRepository;
    @Autowired // 필드 주입 방식
    private DiscountPolicy discountPolicy;
}</code></pre><p>✔️ 일반 메서드 주입
일반 메서드 주입 방식은 일반 메서드를 통해서 주입받는 방식입니다. 
한번에 여러 필드를 주입받을 수 있는 장점이 있지만 일반적으로 잘 사용하지 않는 방식입니다.</p>
<pre><code>@Component
public class OrderServiceImpl implements OrderService {
    private MemberRepository memberRepository;
    private DiscountPolicy discountPolicy;

@Autowired
public void init(MemberRepository memberRepository, DiscountPolicy
discountPolicy) {
    this.memberRepository = memberRepository;
    this.discountPolicy = discountPolicy;
    }
}</code></pre><blockquote>
<p>그렇다면 왜 생성자 주입 방식을 사용해야 할까?</p>
</blockquote>
<p>위 4가지 방식중에서는 필드 주입이 가장 편해 보이지만, 최근에는 DI 프레임워크 대부분이 생성자 주입방식을 권장하고 있습니다.</p>
<ul>
<li>대부분의 의존관계 주입은 한번 일어나면 종료시점까지 의존관계를 변경할 일이 없습니다. (대부분 불변하게 설계하기 때문에)</li>
<li>수정자 주입이나, 일반 메서드 주입 방식은 메서드를 사용해야 하므로 public으로 열어두어야 합니다.</li>
<li>실수로 변경할 수 있기 때문에 변경하면 안되는 메서드를 열어두는 것은 좋은 설계 방법이 아닙니다.</li>
<li>하지만 생성자 주입 방식은 객체를 생성할 때 1번만 호출되기 때문에 불변하게 설계할 수 있습니다.</li>
<li>기본으로 생성자 주입을 하고 필요 할 때 수정자 주입 방식을 옵션으로 부여하는 방식으로 코드를 작성해야 합니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[KPT 회고]]></title>
            <link>https://velog.io/@jeong_uk/KPT-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@jeong_uk/KPT-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Mon, 01 Jul 2024 07:13:26 GMT</pubDate>
            <description><![CDATA[<p>Keep
인프런 강의를 들으면서 공부를 하고, 새롭게 알게된 부분은 
메모하면서 공부함.</p>
<p>Problem
모르는 부분이 많아서 이해하는 시간이 많이 필요했고, 
Spring에 대한 공부가 많이 필요하다고 느꼈음.
새로운 부분을 배우게 되면 낯설다고 두려워하지말기!</p>
<p>Try
멋사 과제를 하면서 운영진분들이 comment로 알려준 방법대로 하면서 코드를 작성해 나아가면 더 나은 코드를 작성하게 될 것 같다. 
백준이나 프로그래머스 알고리즘을 풀면서 자신만의 코딩스타일이 생기면서 실력을 기르면 좋을 것 같다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바 - 메서드(Method)]]></title>
            <link>https://velog.io/@jeong_uk/%EC%9E%90%EB%B0%94-%EB%A9%94%EC%84%9C%EB%93%9CMethod</link>
            <guid>https://velog.io/@jeong_uk/%EC%9E%90%EB%B0%94-%EB%A9%94%EC%84%9C%EB%93%9CMethod</guid>
            <pubDate>Sat, 27 Apr 2024 10:16:30 GMT</pubDate>
            <description><![CDATA[<h4 id="메서드란">메서드란?</h4>
<p>자바에서는 함수를 메서드(Method)라 한다.
메서드를 사용하는 이유는 무엇일까? 바로 코드의 중복을 제거해 준다는 것이다.
코드를 통해서 살펴 보도록 하자.</p>
<p>간단하게 변수 두개를 더해서 출력해주는 기능을 개발해보자.</p>
<pre><code>package method;
public class Method1 {
 public static void main(String[] args) {
 //계산1
 int a = 50;
 int b = 50;
 System.out.println(a + &quot;+&quot; + b + &quot; 연산 수행&quot;);
 int sum1 = a + b;
 System.out.println(&quot;결과1 출력:&quot; + sum1);
 //계산2
 int x = 10; // 중복
 int y = 20; // 중복
 System.out.println(x + &quot;+&quot; + y + &quot; 연산 수행&quot;); // 중복
 int sum2 = x + y; // 중복
 System.out.println(&quot;결과2 출력:&quot; + sum2); // 중복
 }
}</code></pre><p>위 코드의 문제점은 과연 무엇일까? 만약 프로그램에서 이와 같은 계산을 반복해야 한다면 어떨 것인가? 위 코드처럼 코드의 중복이 많아 질 것이다. 이런 문제들을 해결해 준 것이 바로 메서드(Method)이다. </p>
<p>그럼 메서드(Method)를 사용해서 코드를 작성해 보도록 하자.</p>
<pre><code>package method;

public class Method1Ref {
    public static void main(String[] args) {
        //계산1
        int sum = add(5,10);
        System.out.println(&quot;결과1 출력:&quot; + sum);
        //계산2
        int sum2 = add(10,100);
        System.out.println(&quot;결과2 출력:&quot; + sum2);
    }

    //add 메서드
    public static int add(int a, int b) {
        System.out.println(a + &quot;+&quot; + b + &quot; 연산 수행&quot;);
        int sum = a+b;
        return sum;
    }
}</code></pre><p><img src="https://velog.velcdn.com/images/jeong_uk/post/72a2852f-98fb-47a9-a950-d44d38a1329f/image.PNG" alt=""></p>
<h4 id="메서드-선언method-declaration">메서드 선언(Method Declaration)</h4>
<pre><code>public static int add(int a, int b)</code></pre><p><strong>add</strong> 는 메서드의 선언 부분으로 메서드의 이름과 반환 타입, 매개변수를 포함한다.
메서드의 이름은 add로 자신이 기능을 수행할 때 이름을 정해주면 된다.</p>
<p><strong>int</strong> 반환 타입은 Return Type으로 메서드의 실행 결과를 반활 할때 타입을 정해주는 것이다.</p>
<p>매개변수는 <strong>(int a, int b)</strong>로 메서드를 호출할 때 전달되는 입력값을 말한다.</p>
<h4 id="메서드-호출">메서드 호출</h4>
<p>앞에서 정의한 메서드를 실행하려면 이름에 입력값을 전달하면 된다.</p>
<pre><code>int sum = add(5,10);</code></pre><p>파라미터 변수 a에는 5가 전달되고 b에는 10이 전달되면서 메서드가 실행되고,
return을 사용해서 메서드의 실행 결과인 sum을 반환함으로써 15가 출력되는 것이다.</p>
<p>참고로 return 타입이 없을 때에는 void를 사용해야 한다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Java 배열(Array)]]></title>
            <link>https://velog.io/@jeong_uk/Java-%EB%B0%B0%EC%97%B4Array</link>
            <guid>https://velog.io/@jeong_uk/Java-%EB%B0%B0%EC%97%B4Array</guid>
            <pubDate>Sat, 27 Apr 2024 09:43:38 GMT</pubDate>
            <description><![CDATA[<h4 id="배열이란">배열이란?</h4>
<p>배열은 동일한 자료형(Data Type) 변수를 사용해기 위해 데이터를 연속된 공간에 저장하는 자료구조이다.</p>
<p>그렇다면 배열을 사용하는 이유는 무엇일까?
코드를 통해서 비교해보자.<img src="https://velog.velcdn.com/images/jeong_uk/post/0ce4721c-c316-49b7-aae2-b5bc0ce550f2/image.PNG" alt=""></p>
<p>만약 위에 코드처럼 학생을 몇 명 더 추가해야 한다면 변수를 선언하는 부분과 점수를 철력하는 부분의 코드도 추가해야 한다. 학생 수가 증가함에 따라 int형 변수도 선언해야 한다. </p>
<p><img src="https://velog.velcdn.com/images/jeong_uk/post/766ca369-6f8e-4d6e-9d23-ba79a56cc2e3/image.PNG" alt="">
위 코드는 배열을 사용함으로써 코드가 얼마나 줄었는지 확인할 수 있다.</p>
<blockquote>
<p>이렇게 같은 타입의 변수를 반복해서 선언하고 반복해서 사용하는 문제를 해결한 것이 바로 배열이다.</p>
</blockquote>
<h4 id="배열-선언-및-사용">배열 선언 및 사용</h4>
<p>배열을 정의 하는 방법은 크게 2가지 방법이 있다.</p>
<pre><code>자료형[] 변수 = {데이터1, 데이터2, 데이터3, ... };</code></pre><p>이 방법은 데이터들의 값을 알고 있을 때 사용하면 편리하다. 앞에서 작성한 코드와 같은 방법이다. 예를 들어 1반에 있는 학생들의 점수를 저장한다고 가정해보자. 
위에 비유한 내용으로 배열을 생성하는 예제를 살펴보자</p>
<pre><code>public class ArrayEx {
    public static void main(String[] args) {
        int[] students = {50, 60, 70, 80, 90};
            // 인덱스 번호 :0 , 1 , 2 , 3 , 4 

            // 변수 값 사용
        System.out.println(students[0]);
        System.out.println(students[1]); 
        System.out.println(students[2]); 
        System.out.println(students[3]); 
        System.out.println(students[4]);
    }
}</code></pre><p>students라는 int[] 변수에 데이터를 저장하였다. 먼저 여기서 알아야 할 것은 바로 인덱스(index)이다. 인덱스는 데이터를 저장한 순서0 부터 시작하여 1씩 증가되어 만들어진다. 즉, students라는 배열에 0번부터 4번까지의 index 번호를 가진 5개의 공간이 데이터들이 저장되어 있는 것이다.</p>
<p>두번째 정의하는 방벙은 아래와 같다.</p>
<pre><code>자료형[] 변수 = new 자료형[배열크기]
변수[0] = 데이터 값;
변수[1] = 데이터 값;</code></pre><p> 위 방법은 배열의 값은 모르지만 향후 값을 저장하고 싶을 때 주로 사용되는 방법이다. new라는 연산자를 통해서 참조형 변수를 생성하게 되는데 아래 사진처럼 x001과 같은 특정 주소값을 가지게 된다. (실제로는 16진수로 나타나는 주소값을 가진다.)
 <img src="https://velog.velcdn.com/images/jeong_uk/post/d0882224-d9f0-4a3b-8e5f-bb17085b839c/image.PNG" alt=""></p>
<p>new 연산자를 사용해서 배열을 작성해 보도록 하자.</p>
<pre><code>public class Array1Ref1 {
 public static void main(String[] args) {

 int[] students = new int[5]; //배열 생성
 //변수 값 대입
 students[0] = 90;
 students[1] = 80;
 students[2] = 70;
 students[3] = 60;
 students[4] = 50;
 //변수 값 사용
 System.out.println(&quot;학생1 점수: &quot; + students[0]);
 System.out.println(&quot;학생2 점수: &quot; + students[1]);
 System.out.println(&quot;학생3 점수: &quot; + students[2]);
 System.out.println(&quot;학생4 점수: &quot; + students[3]);
 System.out.println(&quot;학생5 점수: &quot; + students[4]);
 }
}</code></pre><p>new는 새로 생성한다는 뜻이고, int[5]는 int형 변수 5개라는 뜻이다. 따라서 int형 변수 5개룰 다룰 수 있는 배열이 생기게 된 것이다.</p>
<p>이제 배열에 대한 간단한 문제를 풀어보도록 하자.</p>
<p>학생5명의 점수를 합산해 평균을 내는 프로그램을 작성해 보도록 하자.</p>
<pre><code>public class ArrayEx1 {
 public static void main(String[] args) {
 int student1 = 90;
 int student2 = 80;
 int student3 = 70;
 int student4 = 60;
 int student5 = 50;
 int total = student1 + student2 + student3 + student4 + student5;
 double average = (double) total / 5;
 System.out.println(&quot;점수 총합: &quot; + total);
 System.out.println(&quot;점수 평균: &quot; + average);
 }
}</code></pre><p><img src="https://velog.velcdn.com/images/jeong_uk/post/b9db80db-b65e-4e45-b084-3e07795e623c/image.PNG" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Java Scanner 학습]]></title>
            <link>https://velog.io/@jeong_uk/Java-Scanner-%ED%95%99%EC%8A%B5</link>
            <guid>https://velog.io/@jeong_uk/Java-Scanner-%ED%95%99%EC%8A%B5</guid>
            <pubDate>Sat, 27 Apr 2024 07:49:36 GMT</pubDate>
            <description><![CDATA[<h3 id="sanner-학습하기">Sanner 학습하기</h3>
<p>사용자의 입력을 받기 위해서는 Scanner가 필요하다.
자바에서는 Sysyem 클래스에 있는 시스템과 관련된 기본 기능들을 제공한다.</p>
<p>표준 입력(System.in), 표준 출력(System.out), 오류 스트림(System.err)등 
Java에서 제공해주는 기능들을 사용할 수 있다.</p>
<p>System.out으로 출력했듯이 System.in을 통해서 사용자의 입력을 받을 수 있다.
하지만 System.in을 통해서 사용자 입력을 받으려면 복잡한 과정이 필요하다.</p>
<p>자바에서는 이러한 문제를 해결해주는 Java에서 제공해주는 java.util에 있는 Scanner 클래스를 제공한다.</p>
<ul>
<li><p>Scanner scanner = new Scanner(System.in);
new Scanner를 사용해서 스캐너 객체를 생성해 scanner 변수에 저장해주고,  Scanner는 System.in을 사용해서 사용자의 입력을 받도록 해준다. </p>
</li>
<li><p>scanner.nextLine() 엔터(\n)을 입력할 때 까지 문자를 가져오는 기능.</p>
</li>
<li><p>scanner.nextInt() 입력을 int형으로 가져오는 기능</p>
</li>
</ul>
<p>다음은 간단한 구구단을 입력받아서 출력해주는 프로그램을 작성해보자.</p>
<pre><code>package scanner.ex;

import java.util.Scanner;

public class ScannerEx4 {
    public static void main(String[] args) {

        Scanner scanner = new Scanner(System.in);
        System.out.print(&quot;구구단의 단 수를 입력해주세요: &quot;);
        int dan = scanner.nextInt();

        for(int hang = 1; hang &lt;=9; hang++){
            System.out.println(dan + &quot; x &quot; + hang + &quot; = &quot; + dan * hang);
        }
    }
}
</code></pre><p>출력 결과<img src="https://velog.velcdn.com/images/jeong_uk/post/fddd0c54-dbbd-4fab-8006-e209cad39eb6/image.PNG" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Java 스코프]]></title>
            <link>https://velog.io/@jeong_uk/Java-%EC%8A%A4%EC%BD%94%ED%94%84</link>
            <guid>https://velog.io/@jeong_uk/Java-%EC%8A%A4%EC%BD%94%ED%94%84</guid>
            <pubDate>Sat, 27 Apr 2024 07:06:12 GMT</pubDate>
            <description><![CDATA[<p>Scope(스코프)란 무엇일까? 변역을 하면 범위라고 나온다.
자바에서 스코프도 마찬가지로 접근 범위라는 것이 있다.</p>
<p>다음 코드에서 분석해 보도록 하자!</p>
<pre><code>public class Scope {
    public static void main(String[] args) {
        int m = 10;
        int temp = 0;
        if (m &gt; 0) {
            temp = m * 2;
        }
        System.out.println(&quot;m = &quot; + m);
        System.out.println(&quot;temp = &quot; + temp);
    }
}</code></pre><p>이 코드는 좋은 코드라고 볼 수 없다.
왜냐하면 temp변수는 if 조건이 만족할 때 임시로 잠깐 사용하는 변수인데 main() 코드 블록에 선언되어 있어서 두 가지 문제가 발생한다.</p>
<ol>
<li><p>비효율적인 메모리 사용: temp 는 if 코드 블록에서만 필요하지만, main() 코드 블록이 종료될 때 까지 메모리에 유지된다. 따라서 불필요한 메모리가 낭비된다.</p>
</li>
<li><p>코드 복잡성:
temp 는 if 코드 블록 안에서만 필요하고, 여기서만 사용하면 된다.
그렇기 때문에 if 코드 블록안에 선언되었다면, temp에 대한 생각을 더이상 하지 않아도 됐는데 더욱 복잡해 진것으로 볼 수 있다. 비록 지금은 코드가 짧아서 복잡해 보일 수 있지만, 만약 코드가 몇 백줄이라고 생각한다면 Scope를 줄이는게 좋은 코드라고 생각할 것이다.</p>
</li>
</ol>
<p>이제 코드에서 Scope를 줄여보도록 하자!</p>
<pre><code>public class Scope {
    public static void main(String[] args) {
        int m = 10;

        if (m &gt; 0) {
            int temp = 0; // 스코프 줄이기
            temp = m * 2;
            System.out.println(&quot;temp = &quot; + temp);
        } // temp 생존 종료
        System.out.println(&quot;m = &quot; + m);
    } m 생존 종료
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[상태코드 자세히 알아보기]]></title>
            <link>https://velog.io/@jeong_uk/%EC%83%81%ED%83%9C%EC%BD%94%EB%93%9C-%EC%9E%90%EC%84%B8%ED%9E%88-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@jeong_uk/%EC%83%81%ED%83%9C%EC%BD%94%EB%93%9C-%EC%9E%90%EC%84%B8%ED%9E%88-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Sat, 10 Feb 2024 13:16:25 GMT</pubDate>
            <description><![CDATA[<p>http 상태코드 자세히 알아보기</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바에서의 상속]]></title>
            <link>https://velog.io/@jeong_uk/%EC%83%81%EC%86%8D%EA%B3%BC-%EB%A9%94%EC%84%9C%EB%93%9C-%EC%98%A4%EB%B2%84%EB%9D%BC%EC%9D%B4%EB%94%A9Overriding</link>
            <guid>https://velog.io/@jeong_uk/%EC%83%81%EC%86%8D%EA%B3%BC-%EB%A9%94%EC%84%9C%EB%93%9C-%EC%98%A4%EB%B2%84%EB%9D%BC%EC%9D%B4%EB%94%A9Overriding</guid>
            <pubDate>Thu, 11 Jan 2024 10:36:10 GMT</pubDate>
            <description><![CDATA[<p>상속은 객체 지향 프로그래밍에서 아주 중요한 요소 중 하나이다. 
기존 클래스의 필드와 메서드를 새로운 클래스에서 재상용하게 해준다면 얼마나 편리할까? 바로 이것이 상속이다. 상속을 하려면 extends 키워드를 사용하면 된다.</p>
<blockquote>
<p>extends 키워드는 대상을 하나만 선택할 수 있다.</p>
</blockquote>
<p>용어 정리</p>
<ul>
<li>부모 클래스(super class) : 상속을 통해 자신의 필드와 메서드를 다른 클래스에 제공하는 클래스</li>
<li>자식 클래스(sub class) : 부모 클래스로부터 필드와 메서드를 상속받는 클래스</li>
</ul>
<p>상속 관계를 알아보기 위해 자동차를 예를 들어 설명하겠다.
자동차에는 여러가지 종류의 자동차를 볼 수 있다. 전기자동차, 가솔린 자동차 등 추상적인 개념으로 보자면 이들은 모두 &quot;자동차&quot;이고 구체적으로 보면 전기, 가솔린로 볼 수 있다. 이들의 공통점으로는 움직이거나, 브레이크는 공통점으로 묶을 수 있고, 충전방식을 다르게 표현할 수 있다. 이제 이걸 상속으로 표현해 보자면, 움직이는 것을 상속받아서 충전하는 부분을 확장해 나가면 되는 것이다.</p>
<p>코드를 보면서 파악해 보도록 하자.</p>
<pre><code>public class Car {
    public void move() {
        System.out.println(&quot;자동차를 움직입니다.&quot;)
    }
}</code></pre><blockquote>
<p>위 코드에서 Cas 클래스는 부모 클래스가 되고, 공통적인 기능은 움직이는 기능 move()가 포함되어 있는 것을 확인할 수 있다.</p>
</blockquote>
<pre><code>public class ElectricCar extedns Car() {
    public void charge() {
        System.out.println(&quot;자동차를 충전합니다.&quot;)
    }
}</code></pre><blockquote>
<p>위 코드 전기차에서는 extends 키워드를 사용하여 Car 클래스를 상속받고, 덕분에 ElectricCar에서도 move()를 사용할  수 있게 되었다.</p>
</blockquote>
<pre><code>public class GasCar extends Car() {
    public void fillup() {
        System.out.println(&quot;자동차를 충전합니다.&quot;)
    }
}</code></pre><blockquote>
<p>위 코드에서도 마찬가지로 extends Car를 사용해서 부모 클래스인 Car를 상속 받았고, 여기서도 move()를 사용할수 있게 되었다.</p>
</blockquote>
<p>상속 구조도<img src="https://velog.velcdn.com/images/jeong_uk/post/05b9c022-0a67-4502-a269-125a2c84c3a8/image.PNG" alt="">
위 사진에서 화살표 방향을 주의해서 살펴보자.
화살표 방향이 이렇게 생긴 이유는 자식 클래스는 부모 클래스로부터 기능을 물려 받아서 접근이 가능하지만, 부모 클래스 입장에서는 누가 상속을 받는지도 모르고 부모 클래스에서 자식 클래스로 접근을 할 수 없기 때문에 방향이 자식 클래스에서 가리키게 된 것이다.</p>
<blockquote>
<p>참고로 자바는 다중 상속이 불가하다!!</p>
</blockquote>
<p>만약 다중 상속이 된다고 생각하면 어떤 일이 벌어지게 될까?
부모 클래스를 2개 상속받는다고 생각해보자. 만약 부모 클래스의 move()기능이 둘 다 있다면 과연 자식 클래스 입장에서는 어떤 부모의 move()를 사용해야 할지 애매한 문제가 발생하게 된다. 이것을 다이아몬드 문제라고 한다. 다중 상속을 하게 되면 이와 같은 애매한 문제와 클래스 계층 구조가 매우 복잡해 질 수 있다. 이런 문제점들 때문에 자바에서는 클래스의 다중 상속을 허용하지 않는다.</p>
<h4 id="상속과-메모리-구조">상속과 메모리 구조</h4>
<p>상속 부분에 있어서 제일 중요하다고 생각하는 부분이 바로 메모리 구조를 파악하는 것이라고 생각한다.</p>
<pre><code>ElectricCar electriCar = new ElectriCar();</code></pre><p><img src="https://velog.velcdn.com/images/jeong_uk/post/4e829e54-21c6-409d-afda-5f78bf574d77/image.PNG" alt=""></p>
<p>위 코드 부분에서 new ElectriCar()를 호출하면 ElectriCar뿐만 아니라 상속 관계에 있는 Car 까지 함께 포함해서 인스턴스를 생성한다. 참조값은 x001로 하나이지만 실제로 그 안에서는 ElectriCar하고 Car두 클래스의 정보가 함께 들어있다는 것이다.
상속이라고 해서 단순하게 부모의 필드와 메서드만을 물려받는 것이 아니라 클래스도 함께 포함해서 생성되는 것이다. 겉으로 보기에는 하나의 인스턴스를 생성하는 것 처럼 보일 수 있지만 실제로는 부모와 자식이 모두 생성되고 공간도 구분되어 있는 것이다.</p>
<pre><code>electricCar.charge()</code></pre><p><img src="https://velog.velcdn.com/images/jeong_uk/post/bfca3f5c-9097-46c4-83cf-433896e43a60/image.PNG" alt=""></p>
<p>electricCar.charge()를 호출하면 참조값인 x001을 확인해서 x001.charge()를 호출한다. 상속 관계의 경우에는 내부에 부모 클래스, 자식 클래스 모두가 존재하기 때문에 접근을 어디에 먼저 할 지를 선택해야 한다. 이때는 호출하는 변수의 타입을 기준으로 선택한다. elecricCar의 변수의 타입이 ElectricCar이기 때문에 인스턴스 내부에 같은 타입은 ElectricCar를 통해서 charge()를 호출한다.</p>
<pre><code>electricCar.move()</code></pre><p>그렇다면 electricCar.move()를 호출하면 어떻게 되는 걸까?
먼저 electricCar.move()룰 호출하게 된다면 참조값인 x001로 먼저 이동하게 된다.
호출하는 변수의 타입은 ElecticCar이기 때문에 타입에 맞게 선택하게 된다.
하지만 ElectricCar에는 move()라는 메서드가 없기 대문에 부모타입으로 올라가서 찾게 된다. 부모 타입인 Car로 올라가서 move()가 있는지 확인후 move()가 있기 때문에 부모에 있는 move()를 호출하게 된다.</p>
<blockquote>
<p>만약 부모에서도 해당 기능을 찾지 못하게 된다면 더 상위 부모로 찾아가게 된다. 계속 올라갔는데도 찾지 못한다면 컴파일 오류를 발생하게 된다.</p>
</blockquote>
<p>상속과 메모리에서 중요한 3가지를 요약해 보았다. </p>
<ul>
<li>상속 관계인 객체를 생성하면 그 내부에는 부모, 자식 모두 생성된다.</li>
<li>상속 관계인 객체를 호출할 때, 대상 타입을 정하고, 이때 호출자의 타입을 통해 대상 타입을 찾는다.</li>
<li>만약 현재 타입에서 기능을 찾지 못한다면, 부모 타입으로 이동하게 되고, 그래도 못찾게 된다면 컴파일 오류를 발생시킨다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[final과 static을 같이 사용하는 이유는 무엇일까?]]></title>
            <link>https://velog.io/@jeong_uk/final%EA%B3%BC-static%EC%9D%84-%EA%B0%99%EC%9D%B4-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0%EB%8A%94-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C</link>
            <guid>https://velog.io/@jeong_uk/final%EA%B3%BC-static%EC%9D%84-%EA%B0%99%EC%9D%B4-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0%EB%8A%94-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C</guid>
            <pubDate>Fri, 05 Jan 2024 18:25:04 GMT</pubDate>
            <description><![CDATA[<p>필자는 여태까지 멤버변수에 있는 상수(Constant)를 그냥 상수라서 final과 static을 같이 사용했었는데, 프로그램을 작성하면서 왜 같이 사용하는지 의문점이 들었다.
그래서 왜 같이 사용하는지 생각해보았는데, 바로 메모리 낭비를 하지 않기위해서 같이 사용하는 것이었다. 
<img src="https://velog.velcdn.com/images/jeong_uk/post/f4576d48-b495-4168-86da-9f4ae032999b/image.PNG" alt=""></p>
<pre><code>public class FieldInit {
 static final int CONST_VALUE = 10;
 final int value = 10;
}</code></pre><ul>
<li>위 코드처럼 value값을 초기화 하는 경우, 모든 인스턴스가 오른쪽 힙 영역 그림과 같이 모든 인스턴스에 value는 10을 가지게 된다.</li>
<li>모든 인스턴스가 같은 값을 사용하기 때문에 결과적으로는 메모리를 낭비하게 된다. 또한 값이 계속 생성되는 것은 개발자가 보기에는 중복으로 보인다!</li>
</ul>
<blockquote>
<h4 id="바로-이럴-때-사용하는-것이-static-final을-같이-사용하는-것이다">바로 이럴 때 사용하는 것이 static final을 같이 사용하는 것이다.</h4>
</blockquote>
<p>MY_VALUE는 static영역에 존재하고, final 키워드를 사용했기 때문에 값이 변하지 않게된다. 즉 static영역은 단 하나만 존재하기 때문에 MY_VALUE변수는 JVM에서 하나만 존재하기 때문에 메모리를 보다 더 효율적으로 사용할 수 있게 되었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Java 메모리 구조]]></title>
            <link>https://velog.io/@jeong_uk/Java-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B5%AC%EC%A1%B0</link>
            <guid>https://velog.io/@jeong_uk/Java-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B5%AC%EC%A1%B0</guid>
            <pubDate>Thu, 28 Dec 2023 16:27:20 GMT</pubDate>
            <description><![CDATA[<p>자바의 메모리 구조에는 크게 메서드 영역, 스택 영역, 힙 영역으로 나눌 수 있다.</p>
<ul>
<li><p>메서드 영역 : 클래스 정보(실행코드), 필드, 메서드와 생성자 등 모든 코디가 존재한다. 또한 static 영역과 상수들을 관리한다.</p>
</li>
<li><p>스택 영역 : 실제 프로그램이 실행되는 영역으로 자바 실행 시, 하나의 실행 스택이 생성된다. 각 스택 프레임은 지역 변수, 메서드 호출 정보 들을 포함한다.</p>
</li>
<li><p>힙 영역 : 객체(인스턴스)가 생성되는 영역이다. new명령어를 사용하면 힙 영역을 사용하게 된다. 만약 참조되지 않는 객체가 있다면 가비지컬렉터에 의해 제거된다.</p>
<p><img src="https://velog.velcdn.com/images/jeong_uk/post/5eeb0d65-2965-4aa5-a30e-47ea6ada30c7/image.PNG" alt="">
힙 영역에는 만약 객체가 100개 생성된다면 힙 메모리에 100개의 객체(인스턴스)가 생기게 된다. 같은 클래스로 부터 생성된 객체라도, 인스턴스 내부의 변수 값은 서로 다를 수 있지만, 메서드는 공통된 코드를 공유한다. 따라서 메서드는 메서드 영역에서 공통으로 관리되고 실행된다.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[this 와 this()]]></title>
            <link>https://velog.io/@jeong_uk/%EC%98%A4%EB%A5%98-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@jeong_uk/%EC%98%A4%EB%A5%98-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 21 Dec 2023 16:50:50 GMT</pubDate>
            <description><![CDATA[<p>먼저 this에 대해서 알아보도록 하자.</p>
<p>멤버 변수와 메서드의 매개변수의 이름이 같으면 둘은 어떻게 구분해야 할까?
먼저 코드를 보도록하자. 다음 코드는 학생 객체를 생성하는 프로그램이다.</p>
<pre><code>package construct;
public class StudentInformation {
 String name;
 int age;
 int grade;


 public StudentInformation(String name, int age, int grade) {
 name = name;
 age = age;
 grade = grade;
 }
}</code></pre><blockquote>
</blockquote>
<h4 id="위-코드에서-생성자를-보면-멤버-변수와-생성자의-매개변수-이름이-같은-것을-볼-수-있다-과연-어떻게-구분해야-할까">위 코드에서 생성자를 보면 멤버 변수와 생성자의 매개변수 이름이 같은 것을 볼 수 있다. 과연 어떻게 구분해야 할까?</h4>
<ul>
<li>이 경우에는 멤버 변수보다 매개변수의 코드가 코드 블럭의 더 안쪽에 있기 때문에 매개변수가 우선순위를 가진다. 따라서 StudentInformation(String name, ...) 생성자 안에서 name이라고 적으면 매개변수에 접근하게 된다.</li>
<li>멤버 변수에 접근하려면 앞에 this. 이라고 붙여주면 된다. this는 인스턴스(객체) 자신의 참조값을 가리킨다.
이렇게 생각하면 this를 왜 사용하는지 알게 되었을 것이다.</li>
</ul>
<h4 id="코드를-다음과-같이-변경해주면-된다">&gt; 코드를 다음과 같이 변경해주면 된다.</h4>
<pre><code>package construct;
public class StudentInformation {
 String name;
 int age;
 int grade;


 public StudentInformation(String name, int age, int grade) {
 this.name = name;
 this.age = age;
 this.grade = grade;
 }
}</code></pre><h4 id="그렇다면-과연-this는-무엇일까">&gt; 그렇다면 과연 this()는 무엇일까?</h4>
<p>메소드 오버로딩을 하는 것 처럼 생성자도 매개변수만 다르게 해서 여러 생성자를 제공할 수 있다.</p>
<pre><code>package construct;
public class StudentInformation {
 String name;
 int age;
 int grade;

 public StudentInformation(String name, int age){
 this.name = name;
 this.age = age;
 this.grade = 0;
 }

 public StudentInformation(String name, int age, int grade) {
 this.name = name;
 this.age = age;
 this.grade = grade;
 }
}</code></pre><h4 id="위에-코드를-보면-반복되는-부분을-찾을-수-있다">위에 코드를 보면 반복되는 부분을 찾을 수 있다.</h4>
<pre><code> this.name = name;
 this.age = age;</code></pre><p>개발자가 제일 싫어하는 것이 무엇인가? 바로 중복코드이다!! 
이때 this()라는 기능을 사용하면 생성자 내부에서 자신의 생성자를 호출할 수 있다.
참고로 this는 인스턴스 자신의 참조값을 가리킨다. 따라서 자신의 생성자를 호출한다고 생각하면 된다. 이제 코드를 수정해 보도록 하자.</p>
<pre><code>package construct;
public class StudentInformation {
 String name;
 int age;
 int grade;

 public StudentInformation(String name, int age){
 this(name,age,0);
 }
 /
 public StudentInformation(String name, int age, int grade) {
 this.name = name;
 this.age = age;
 this.grade = grade;
 }
}</code></pre><p>this()를 사용하면 생성자 내부에서 다른 생성자를 호출 할 수 있다.
첫번째 생성자에서 두번째 생성자를 호출한 것이다. 이 부분을 잘 활용한다면 지금과 같은 중복코드를 제거할 수 있다.</p>
<p>this()를 사용하려면 주의사항이 있다. 명심하도록 하자.</p>
<blockquote>
<p>this()는 생성자 코드의 첫줄에만 작성이 가능하다.
다음은 컴파일 에러가 발생하는 코드이다.</p>
</blockquote>
<pre><code>System.out.println(&quot;에러가 발생하는 코드입니다.);
public StudentInformation(String name, int age){
 this(name,age,0);</code></pre>]]></description>
        </item>
    </channel>
</rss>