<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>baby_potato</title>
        <link>https://velog.io/</link>
        <description>개발감자</description>
        <lastBuildDate>Wed, 05 Mar 2025 10:02:40 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>baby_potato</title>
            <url>https://velog.velcdn.com/images/baby_potato/profile/2d60b24d-72a0-4fa9-af43-de036a39ee80/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. baby_potato. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/baby_potato" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[개인-프로젝트-로그인-기능-구현]]></title>
            <link>https://velog.io/@baby_potato/%EA%B0%9C%EC%9D%B8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84</link>
            <guid>https://velog.io/@baby_potato/%EA%B0%9C%EC%9D%B8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84</guid>
            <pubDate>Wed, 05 Mar 2025 10:02:40 GMT</pubDate>
            <description><![CDATA[<h1 id="bcrypt-인증과-cors-해결">BCrypt 인증과 CORS 해결</h1>
<h2 id="📌-백엔드-구현-단계">📌 <strong>백엔드 구현 단계</strong></h2>
<blockquote>
<p>BCrypt로 비밀번호 암호화 및 검증
Spring Data JPA와 MySQL 사용
CORS 문제 해결</p>
</blockquote>
<h3 id="1-db-생성">1. DB 생성</h3>
<pre><code>```sql
CREATE TABLE user (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(100) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL
);
```</code></pre><h3 id="2-의존성-추가">2. 의존성 추가</h3>
<p>&lt;pom.xml&gt;</p>
<pre><code class="language-xml">&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
    &lt;artifactId&gt;spring-boot-starter-security&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.security&lt;/groupId&gt;
    &lt;artifactId&gt;spring-security-crypto&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre>
<h3 id="3-security-설정">3. Security 설정</h3>
<p>&lt;SecurityConfig.java&gt;</p>
<pre><code class="language-java">package com.dearu.backend.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .csrf().disable()  // CSRF 비활성화
                .authorizeHttpRequests()
                .requestMatchers(&quot;/api/users/**&quot;, &quot;/api/invitations/**&quot;).permitAll()  // 사용자, 초대장 API는 인증 없이 접근 허용
                .anyRequest().authenticated()
                .and()
                .formLogin().disable(); // 기본 로그인 폼 비활성화
        return http.build();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}</code></pre>
<h3 id="4-회원-엔티티-및-dto-작성">4. 회원 엔티티 및 DTO 작성</h3>
<p>&lt;User.java&gt;</p>
<ul>
<li>BCrypt를 사용하여 비밀번호 암호화 및 검증<pre><code class="language-java">package com.dearu.backend.model;
</code></pre>
</li>
</ul>
<p>import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;</p>
<p>@Entity
@Table(name = &quot;user&quot;)
@Getter
@Setter
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;</p>
<pre><code>@Column(unique = true, nullable = false)
private String username;

@Column(nullable = false)
private String password;</code></pre><p>}</p>
<pre><code>&lt;UserDTO.java&gt;
```java
package com.dearu.backend.dto;

import lombok.Data;

@Data
public class UserDTO {
    private String username;
    private String password;
}</code></pre><h3 id="5-회원-저장-및-조회">5. 회원 저장 및 조회</h3>
<p>&lt;UserRepository.java&gt;</p>
<pre><code class="language-java">package com.dearu.backend.repository;

import com.dearu.backend.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends JpaRepository&lt;User, Long&gt; {
    User findByUsername(String username);
}</code></pre>
<p>&lt;UserService.java&gt;</p>
<ul>
<li>BCrypt로 비밀번호를 검증<pre><code class="language-java">package com.dearu.backend.service;
</code></pre>
</li>
</ul>
<p>import com.dearu.backend.model.User;
import com.dearu.backend.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;</p>
<p>@Service
public class UserService {</p>
<pre><code>@Autowired
private UserRepository userRepository;

@Autowired
private PasswordEncoder passwordEncoder;

// 회원가입
public User register(User user) {
    user.setPassword(passwordEncoder.encode(user.getPassword()));
    return userRepository.save(user);
}

// 로그인
public boolean login(String username, String rawPassword) {
    User user = userRepository.findByUsername(username);
    if (user != null) {
        return passwordEncoder.matches(rawPassword, user.getPassword());
    }
    return false;
}</code></pre><p>}</p>
<pre><code>
### 6. BCrypt를 이용한 비밀번호 암호화 및 비교
- **회원가입:** `passwordEncoder.encode(rawPassword)` 사용하여 암호화 후 저장
- **로그인:** `passwordEncoder.matches(rawPassword, encodedPassword)` 사용하여 비교
### 7. 로그인 API
&lt;UserController.java&gt;
```java
package com.dearu.backend.controller;

import com.dearu.backend.dto.UserDTO;
import com.dearu.backend.model.User;
import com.dearu.backend.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@CrossOrigin(origins = &quot;http://localhost:3001&quot;)
@RestController
@RequestMapping(&quot;/api/users&quot;)
public class UserController {

    @Autowired
    private UserService userService;

    // 회원가입
    @PostMapping(&quot;/register&quot;)
    public String register(@RequestBody UserDTO userDTO) {
        User user = new User();
        user.setUsername(userDTO.getUsername());
        user.setPassword(userDTO.getPassword());
        userService.register(user);
        return &quot;회원가입 성공!&quot;;
    }

    // 로그인
    @PostMapping(&quot;/login&quot;)
    public String login(@RequestBody UserDTO userDTO) {
        boolean isAuthenticated = userService.login(userDTO.getUsername(), userDTO.getPassword());
        return isAuthenticated ? &quot;로그인 성공!&quot; : &quot;로그인 실패!&quot;;
    }
}</code></pre><h3 id="8-postman-test">8. PostMan Test</h3>
<p>&lt;회원가입&gt;
<img src="https://velog.velcdn.com/images/baby_potato/post/224e13dc-be35-47b4-a715-58ca6f6cb693/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/baby_potato/post/d575b728-e0a9-4396-b1e4-0f733fc8862d/image.png" alt=""></p>
<p>&lt;로그인&gt;
<img src="https://velog.velcdn.com/images/baby_potato/post/fa1b97b0-3c05-4ffc-b162-725d66486714/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/baby_potato/post/128c59f9-dccd-4556-96e9-5a575466e6a2/image.png" alt=""></p>
<h2 id="📌-프론트엔드-구현-단계---매우-간단하게-구현함">📌 프론트엔드 구현 단계 - 매우 간단하게 구현함</h2>
<blockquote>
<p>Axios를 사용한 HTTP 요청
로그인 UI 및 상태 관리
로그인 성공 시 토큰 저장 및 페이지 이동</p>
</blockquote>
<h3 id="1-api-연결-설정">1. API 연결 설정</h3>
<p>&lt;api.ts&gt;</p>
<pre><code class="language-ts">// 회원가입
export const registerUser = async (userData: { username: string; password: string }) =&gt; {
    try {
        const response = await axios.post(`${API_BASE_URL}/users/register`, userData, {
            headers: {
                &quot;Content-Type&quot;: &quot;application/json&quot;
            },
            withCredentials: true
        });
        return response.data;
    } catch (error) {
        console.error(&quot;Error registering user:&quot;, error);
        throw error;
    }
};

// 로그인
export const loginUser = async (userData: { username: string; password: string }) =&gt; {
    try {
        const response = await axios.post(`${API_BASE_URL}/users/login`, userData, {
            headers: {
                &quot;Content-Type&quot;: &quot;application/json&quot;
            },
            withCredentials: true
        });
        return response.data;
    } catch (error) {
        console.error(&quot;Error logging in:&quot;, error);
        throw error;
    }
};</code></pre>
<h3 id="2-login-컴포넌트-작성">2. Login 컴포넌트 작성</h3>
<p>&lt;Login.tsx&gt;</p>
<ul>
<li>로그인 성공 시 토큰을 Local Storage에 저장<pre><code class="language-tsx">import React, { useState } from &quot;react&quot;;
import { loginUser } from &quot;../services/api&quot;;
import { useNavigate } from &quot;react-router-dom&quot;;
</code></pre>
</li>
</ul>
<p>const Login: React.FC = () =&gt; {
    const [username, setUsername] = useState(&quot;&quot;);
    const [password, setPassword] = useState(&quot;&quot;);
    const navigate = useNavigate();</p>
<pre><code>const handleLogin = async (event: React.FormEvent) =&gt; {
    event.preventDefault();
    try {
        const response = await loginUser({ username, password });
        alert(response); // 로그인 성공 메시지
        navigate(&quot;/&quot;);   // 로그인 성공 시 홈으로 이동
    } catch (error) {
        alert(&quot;로그인 실패. 다시 시도하세요.&quot;);
    }
};

return (
    &lt;div className=&quot;login-container&quot;&gt;
        &lt;h2&gt;로그인&lt;/h2&gt;
        &lt;form onSubmit={handleLogin}&gt;
            &lt;input
                type=&quot;text&quot;
                placeholder=&quot;아이디&quot;
                value={username}
                onChange={(e) =&gt; setUsername(e.target.value)}
                required
            /&gt;
            &lt;input
                type=&quot;password&quot;
                placeholder=&quot;비밀번호&quot;
                value={password}
                onChange={(e) =&gt; setPassword(e.target.value)}
                required
            /&gt;
            &lt;button type=&quot;submit&quot;&gt;로그인&lt;/button&gt;
        &lt;/form&gt;
        &lt;p&gt;
            계정이 없으신가요? &lt;a href=&quot;/register&quot;&gt;회원가입&lt;/a&gt;
        &lt;/p&gt;
    &lt;/div&gt;
);</code></pre><p>};</p>
<p>export default Login;</p>
<pre><code>
### 3. Register 컴포넌트 작성 
&lt;Register.tsx&gt;
```tsx
import React, { useState } from &quot;react&quot;;
import { registerUser } from &quot;../services/api&quot;;
import { useNavigate } from &quot;react-router-dom&quot;;

const Register: React.FC = () =&gt; {
    const [username, setUsername] = useState(&quot;&quot;);
    const [password, setPassword] = useState(&quot;&quot;);
    const navigate = useNavigate();

    const handleRegister = async (event: React.FormEvent) =&gt; {
        event.preventDefault();
        try {
            const response = await registerUser({ username, password });
            alert(response); // 회원가입 성공 메시지
            navigate(&quot;/login&quot;); // 회원가입 후 로그인 페이지로 이동
        } catch (error) {
            alert(&quot;회원가입 실패. 다시 시도하세요.&quot;);
        }
    };

    return (
        &lt;div className=&quot;register-container&quot;&gt;
            &lt;h2&gt;회원가입&lt;/h2&gt;
            &lt;form onSubmit={handleRegister}&gt;
                &lt;input
                    type=&quot;text&quot;
                    placeholder=&quot;아이디&quot;
                    value={username}
                    onChange={(e) =&gt; setUsername(e.target.value)}
                    required
                /&gt;
                &lt;input
                    type=&quot;password&quot;
                    placeholder=&quot;비밀번호&quot;
                    value={password}
                    onChange={(e) =&gt; setPassword(e.target.value)}
                    required
                /&gt;
                &lt;button type=&quot;submit&quot;&gt;회원가입&lt;/button&gt;
            &lt;/form&gt;
            &lt;p&gt;
                이미 계정이 있으신가요? &lt;a href=&quot;/login&quot;&gt;로그인&lt;/a&gt;
            &lt;/p&gt;
        &lt;/div&gt;
    );
};

export default Register;</code></pre><h3 id="4-react-router-설정">4. React Router 설정</h3>
<p>&lt;App.tsx&gt;</p>
<ul>
<li>react-router-dom을 사용하여 페이지 이동<pre><code class="language-tsx">{/* ✅ 로그인 &amp; 회원가입 추가 */}
&lt;Route path=&quot;/login&quot; element={&lt;Login /&gt;} /&gt;
&lt;Route path=&quot;/register&quot; element={&lt;Register /&gt;} /&gt;</code></pre>
<h3 id="5-화면-테스트">5. 화면 테스트</h3>
&lt;로그인&gt;
<img src="https://velog.velcdn.com/images/baby_potato/post/93492366-d333-49ae-ae22-1b90d39254f0/image.png" alt="">
<img src="https://velog.velcdn.com/images/baby_potato/post/2bddad9d-1b6e-4619-9c6d-104f81fd79b8/image.png" alt="">
&lt;회원가입&gt;
<img src="https://velog.velcdn.com/images/baby_potato/post/f0437805-7434-4df6-9444-9c0f0dda0f05/image.png" alt="">
<img src="https://velog.velcdn.com/images/baby_potato/post/78dafcc0-c7a0-4d89-b7af-42a85fb82614/image.png" alt="">
<img src="https://velog.velcdn.com/images/baby_potato/post/490e35ea-f9f5-4d7e-88d6-cb01020f5d09/image.png" alt=""></li>
</ul>
<h3 id="6-cors-문제-해결-spring-boot">6. <strong>CORS 문제 해결 (Spring Boot)</strong></h3>
<ol>
<li><p>Controller 에 @CrossOrigin 어노테이션 사용</p>
<pre><code class="language-java">@CrossOrigin(origins = &quot;http://localhost:3000&quot;)</code></pre>
</li>
<li><p>WebConfig.java에서 글로벌 설정</p>
<pre><code class="language-java">@Configuration
public class WebConfig {

 @Bean
 public WebMvcConfigurer corsConfigurer() {
     return new WebMvcConfigurer() {
         @Override
         public void addCorsMappings(CorsRegistry registry) {
             registry.addMapping(&quot;/**&quot;)
                     .allowedOrigins(&quot;http://localhost:3000&quot;)
                     .allowedMethods(&quot;GET&quot;, &quot;POST&quot;, &quot;PUT&quot;, &quot;DELETE&quot;)
                     .allowCredentials(true);
         }
     };
 }
}</code></pre>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[개인-프로젝트-start]]></title>
            <link>https://velog.io/@baby_potato/%EA%B0%9C%EC%9D%B8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8</link>
            <guid>https://velog.io/@baby_potato/%EA%B0%9C%EC%9D%B8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8</guid>
            <pubDate>Wed, 05 Mar 2025 09:32:07 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>초대장 앱을 만들어 보자!</p>
</blockquote>
<h1 id="💡-주요-기능-concept">💡 주요 기능 (concept)</h1>
<h2 id="1-초대장-생성-및-관리">1. 초대장 생성 및 관리</h2>
<ul>
<li><p>사용자가 이벤트(결혼식, 돌잔치, 생일 등)를 선택하여 초대장 생성 가능</p>
</li>
<li><p>초대장 꾸미기 및 미리보기 기능</p>
<h2 id="2초대장-전송-및-rsvp-관리">2.초대장 전송 및 RSVP 관리</h2>
</li>
<li><p>이메일, 문자(SMS), 카카오톡, 링크 공유 방식으로 초대장 전송</p>
</li>
<li><p>참석 여부(RSVP) 응답 기능 (참석 / 불참 / 미정)</p>
</li>
<li><p>참석자 목록 관리 (참석자 수 카운트 및 실시간 업데이트)</p>
</li>
</ul>
<h2 id="3-일정-및-알림-시스템">3. 일정 및 알림 시스템</h2>
<ul>
<li>캘린더 연동 (Google Calendar, Outlook 등)</li>
<li>초대장 받은 사람에게 일정 리마인더 알림 전송</li>
</ul>
<h2 id="4-추가기능">4. 추가기능</h2>
<ul>
<li>개발 진행 하면서 추가 기능 개발 예정..</li>
</ul>
<h1 id="🛠️-기술-스택">🛠️ 기술 스택</h1>
<h2 id="📌-프론트엔드-react">📌 프론트엔드 (React)</h2>
<ul>
<li><strong>UI 라이브러리</strong>: Material-UI, Ant Design 또는 TailwindCSS</li>
<li><strong>상태 관리</strong>: Redux 또는 React Context API</li>
<li><strong>지도 API</strong>: Google Maps API 또는 Kakao 지도 API</li>
<li><strong>이미지/동영상 업로드</strong>: Firebase Storage 또는 AWS S3</li>
</ul>
<h2 id="📌-백엔드-java-spring-boot">📌 백엔드 (Java, Spring Boot)</h2>
<ul>
<li><strong>API 설계</strong>: RESTful API 또는 GraphQL</li>
<li><strong>데이터베이스</strong>: MySQL (참석자 및 이벤트 데이터 저장)</li>
<li><strong>이메일/SMS 발송</strong>: Twilio, SendGrid, AWS SES</li>
<li><strong>결제 시스템</strong>: Stripe, Toss Payments (축의금, 선물 기능 추가 시)</li>
<li><strong>실시간 알림</strong>: WebSocket 또는 Firebase Cloud Messaging (FCM)</li>
</ul>
<h1 id="🛠️-프로젝트-구조">🛠️ 프로젝트 구조</h1>
<pre><code class="language-txt">/dearU
 ├── frontend (React + TypeScript)
 │   ├── src
 │   │   ├── components  (재사용 가능한 UI 컴포넌트)
 │   │   ├── pages       (각 페이지 라우트)
 │   │   ├── assets      (이미지, 아이콘 등)
 │   │   ├── store       (상태 관리)
 │   │   ├── services    (API 호출)
 │   │   ├── types       (TypeScript 타입 정의)
 │   ├── tsconfig.json  (TypeScript 설정)
 │   ├── package.json
 │
 ├── backend (Spring Boot)
 │   ├── src/main/java/com/dearu
 │   │   ├── config              
 │   │   ├── controller  (API 엔드포인트)
 │   │   ├── service     (비즈니스 로직)
 │   │   ├── repository  (DB 연동)
 │   │   ├── model       (데이터 모델)
 │   ├── pom.xml
 │
 ├── README.md</code></pre>
<h1 id="개발-진행">개발 진행</h1>
<h2 id="1-프론트-환경-구성reacttypescript">1. 프론트 환경 구성(React+TypeScript)</h2>
<pre><code>npx create-react-app frontend --template typescript --use-npm
cd frontend
npm install react@18 react-dom@18
npm install --save-dev @types/react@18 @types/react-dom@18

npm start</code></pre><h2 id="2-github-연동">2. GitHub 연동</h2>
<p><a href="https://github.com/sein0121/dearU">https://github.com/sein0121/dearU</a></p>
<h2 id="3-백엔드-환경-구성srpingboot">3. 백엔드 환경 구성(SrpingBoot)</h2>
<h3 id="3-1-프로젝트-생성">3-1. 프로젝트 생성</h3>
<p><img src="https://velog.velcdn.com/images/baby_potato/post/67fd0300-15b2-4cdd-9001-795239767d9b/image.png" alt="">
<img src="https://velog.velcdn.com/images/baby_potato/post/f76d503b-dcdc-4e20-9d74-e3171c8b1690/image.png" alt=""></p>
<h3 id="3-2-mysql-설정">3-2. MySQL 설정</h3>
<p>application.properties</p>
<pre><code class="language-txt"># ✅ MySQL 연결 정보
spring.datasource.url=jdbc:mysql://localhost:3306/dearu?serverTimezone=UTC&amp;characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=your_password  # ← 본인의 MySQL 비밀번호 입력

# ✅ JPA 설정
spring.jpa.hibernate.ddl-auto=update  # 애플리케이션 실행 시 자동 테이블 생성
spring.jpa.show-sql=true  # 콘솔에 SQL 출력
spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect
spring.sql.init.mode=always  # 초기 SQL 실행 (선택사항)

# ✅ 서버 포트 설정 (기본값: 8080)
server.port=8080</code></pre>
<h3 id="3-3-프론트와-연결">3-3. 프론트와 연결</h3>
<pre><code>npm install axios</code></pre><p>api.ts 생성</p>
<pre><code class="language-tsx">import axios from &quot;axios&quot;;

const API_BASE_URL = &quot;http://localhost:8080/api&quot;;

export const getEvents = async () =&gt; {
    const response = await axios.get(`${API_BASE_URL}/events`);
    return response.data;
};

export const createEvent = async (event: { title: string; description: string }) =&gt; {
    const response = await axios.post(`${API_BASE_URL}/events`, event);
    return response.data;
};</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[ Spring Boot 기반 개인 투자자 트랜드 분석 서비스 개발기]]></title>
            <link>https://velog.io/@baby_potato/personalinvestor</link>
            <guid>https://velog.io/@baby_potato/personalinvestor</guid>
            <pubDate>Tue, 18 Feb 2025 05:19:14 GMT</pubDate>
            <description><![CDATA[<h1 id="개인투자자들의-트랜드-분석-서비스">개인투자자들의 트랜드 분석 서비스</h1>
<h2 id="개요">개요</h2>
<blockquote>
<p>개별 투자자들의 투자 트랜드를 분석하여 &quot;최근 개인이 많이 매수한 종목, 매매동향, 관심종목, 관련 뉴스 등&quot;을 보여주는 서비스</p>
</blockquote>
<h3 id="주요-기능">주요 기능</h3>
<ol>
<li>최근 1주일/1개월 개인 투자자 순매수 상위 종목 조회</li>
<li>기관 &amp; 외국인 투자잦와 비교하여 매매 동향 분석</li>
<li>AI 기반 투자자 관심 종목 추천</li>
<li>관심종목 저장 및 조회</li>
<li>사용자의 관심 종목 기반 뉴스 크롤링</li>
</ol>
<h3 id="활용-데이터">활용 데이터</h3>
<p>한국 거래소 - 투자자별 매매동향 , 네이버 금융 크롤링</p>
<h3 id="개발-스택">개발 스택</h3>
<ul>
<li>언어: Java 17+</li>
<li>프레임워크: Spring Boot 3.x</li>
<li>데이터베이스: MySQL </li>
<li>API 호출: RestTemplate 또는 WebClient (KRX 데이터 가져오기)</li>
<li>배포: 로컬 서버 실행</li>
</ul>
<h2 id="개발">개발</h2>
<h3 id="프로젝트-초기-설정">프로젝트 초기 설정</h3>
<blockquote>
<p>Spring Boot를 사용한 RESTful API 형태로 개발 진행</p>
</blockquote>
<ol>
<li>프로젝트 생성 및 세팅
<img src="https://velog.velcdn.com/images/baby_potato/post/d55000c4-34d1-466d-99fb-61af01166198/image.png" alt=""></li>
</ol>
<p>vscode 에서 프로젝트 열고 src/main/resources/application.properties 수정</p>
<pre><code># MySQL 데이터베이스 설정
spring.datasource.url=jdbc:mysql://localhost:3306/stock_trend?useSSL=false&amp;serverTimezone=Asia/Seoul
spring.datasource.username=root
spring.datasource.password=yourpassword
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# JPA 설정
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect
</code></pre><p>mysql 접속 후 </p>
<pre><code class="language-sql">CREATE DATABASE stock_trend;</code></pre>
<p>터미널에 </p>
<pre><code>.\mvnw.cmd spring-boot:run</code></pre><h4 id="test-코드">test 코드</h4>
<p>&lt;Controller/TrendContrller.java&gt;</p>
<pre><code class="language-java">package com.example.personal_investor_trend;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.Map;

@RestController
@RequestMapping(&quot;/api/trend&quot;)
public class TrendController {

    @GetMapping(&quot;/top-buyers&quot;)
    public List&lt;Map&lt;String, String&gt;&gt; getTopBuyers() {
        return List.of(
                Map.of(&quot;ticker&quot;, &quot;005930&quot;, &quot;name&quot;, &quot;삼성전자&quot;, &quot;net_buy&quot;, &quot;150억&quot;),
                Map.of(&quot;ticker&quot;, &quot;000660&quot;, &quot;name&quot;, &quot;SK하이닉스&quot;, &quot;net_buy&quot;, &quot;120억&quot;)
        );
    }

    @GetMapping(&quot;/comparison&quot;)
    public Map&lt;String, String&gt; getComparison() {
        return Map.of(
                &quot;개인 투자자&quot;, &quot;삼성전자 +150억&quot;,
                &quot;기관 투자자&quot;, &quot;삼성전자 -80억&quot;,
                &quot;외국인 투자자&quot;, &quot;삼성전자 -70억&quot;
        );
    }
}</code></pre>
<p><img src="https://velog.velcdn.com/images/baby_potato/post/44200044-fb62-4c31-9f72-5e62ae24024b/image.png" alt=""></p>
<h3 id="krx-데이터-가져오기--저장하기">KRX 데이터 가져오기 &amp; 저장하기</h3>
<h4 id="1-krx-투자자별-매매-동향-데이터-가져오기">1. KRX 투자자별 매매 동향 데이터 가져오기</h4>
<ul>
<li><p><a href="http://openapi.krx.co.kr/contents/OPP/MAIN/main/index.cmd">http://openapi.krx.co.kr/contents/OPP/MAIN/main/index.cmd</a> 에서 인증키 받고 원하는 데이터 api 신청</p>
</li>
<li><p>&lt;KrxService.java&gt;</p>
<pre><code class="language-java">         package com.example.personal_investor_trend.service;

          import org.springframework.http.*;
          import org.springframework.stereotype.Service;
          import org.springframework.web.client.RestTemplate;

          import java.util.Collections;
          import java.util.Map;

          @Service
          public class KrxService {

              private final RestTemplate restTemplate = new RestTemplate();
              private static final String API_URL = &quot;http://data-dbg.krx.co.kr/svc/apis/sto/ksq_bydd_trd&quot;; // KRX OpenAPI URL
  public String getKrxData(String basDd) {
                  //요청 데이터(JSON)
                  Map&lt;String, String&gt; requestBody = Collections.singletonMap(&quot;basDd&quot;, basDd);

                  //HTTP 헤더
                  HttpHeaders headers = new HttpHeaders();
                  headers.setContentType(MediaType.APPLICATION_JSON);
                   headers.set(&quot;Authorization&quot;, &quot;Bearer API인증키&quot;);

                  //HTTP 요청 객체 
                  HttpEntity&lt;Map&lt;String, String&gt;&gt; requestEntity = new HttpEntity&lt;&gt;(requestBody, headers);

                  //API 호출
                  ResponseEntity&lt;String&gt; response = restTemplate.exchange(API_URL, HttpMethod.POST, requestEntity, String.class);

                  return response.getBody();

              }
          }</code></pre>
</li>
<li><p>&lt;TrendController.java&gt;</p>
<pre><code class="language-java">package com.example.personal_investor_trend.controller;
</code></pre>
</li>
</ul>
<p>import org.springframework.web.bind.annotation.*;</p>
<p>import com.example.personal_investor_trend.service.KrxService;</p>
<p>import java.util.List;
import java.util.Map;</p>
<p>@RestController
@RequestMapping(&quot;/api/trend&quot;)
public class TrendController {</p>
<p>   private final KrxService krxService;</p>
<p>   public TrendController(KrxService krxService){
    this.krxService = krxService;
   }</p>
<p>   @PostMapping(&quot;/krx&quot;)
    public String getKrxData(@RequestBody Map&lt;String, String&gt; request) {
        return krxService.getKrxData(request.get(&quot;basDd&quot;));
    }
}</p>
<pre><code>* 결과 확인 
    * Method : POST
    * URL : http://localhost:8080/api/trend/krx
    * Header : Content-Type: application/json , Authorization: Bearer YOUR_API_KEY
    * Body (raw JSON) 

        ```
      {
           &quot;basDd&quot;: &quot;20250210&quot;
         }

![](https://velog.velcdn.com/images/baby_potato/post/d7f3f8df-8c7e-493a-a512-cfb11e4b0b3d/image.png)

#### 2. 데이터 적재
&lt;StockTransaction.java&gt;
```java
package com.example.personal_investor_trend.model;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Entity
@Getter
@Setter
@NoArgsConstructor
@Table(name = &quot;stock_transactions&quot;)
public class StockTransaction {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String basDd;   // 기준일자
    private String isuCd;   // 종목코드
    private String isuNm;   // 종목명
    private String mktNm;   // 시장 구분
    private Long accTrdVol; // 거래량
    private Long mktCap;    // 시가총액
}</code></pre><p>&lt;StockTransactionRepository.java&gt;</p>
<pre><code class="language-java">package com.example.personal_investor_trend.repository;

import com.example.personal_investor_trend.model.StockTransaction;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface StockTransactionRepository extends JpaRepository&lt;StockTransaction, Long&gt; {
    List&lt;StockTransaction&gt; findByBasDd(String basDd);
}</code></pre>
<p>&lt;KrxService.java&gt;</p>
<pre><code class="language-java">package com.example.personal_investor_trend.service;

import com.example.personal_investor_trend.model.StockTransaction;
import com.example.personal_investor_trend.repository.StockTransactionRepository;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collections;
import java.util.List;
import java.util.Map;

@Service
public class KrxService {

    private final RestTemplate restTemplate = new RestTemplate();
    private static final String API_URL = &quot;http://data-dbg.krx.co.kr/svc/apis/sto/ksq_bydd_trd&quot;; // KRX OpenAPI URL

    private final StockTransactionRepository repository;

    public KrxService(StockTransactionRepository repository){
        this.repository = repository;
    }

    public String getKrxData(String basDd) {
        //요청 데이터(JSON)
        Map&lt;String, String&gt; requestBody = Collections.singletonMap(&quot;basDd&quot;, basDd);

        //HTTP 헤더
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
         headers.set(&quot;Authorization&quot;, &quot;Bearer D6274193FC6A4043B50121146C47226E67C50DB5&quot;);

        //HTTP 요청 객체 
        HttpEntity&lt;Map&lt;String, String&gt;&gt; requestEntity = new HttpEntity&lt;&gt;(requestBody, headers);

        //API 호출
        ResponseEntity&lt;String&gt; response = restTemplate.exchange(API_URL, HttpMethod.POST, requestEntity, String.class);

        // 데이터 저장 로직 실행
        return saveKrxData(response.getBody(), basDd);

    }

    @Transactional
    public String saveKrxData(String jsonResponse, String basDd){
        try{
            ObjectMapper objectMapper = new ObjectMapper();
            JsonNode rootNode = objectMapper.readTree(jsonResponse).get(&quot;OutBlock_1&quot;);

            for (JsonNode node: rootNode){
                StockTransaction transaction = new StockTransaction();
                transaction.setBasDd(basDd);
                // transaction.setBasDd(node.get(&quot;BAS_DD&quot;).asText());
                transaction.setIsuCd(node.get(&quot;ISU_CD&quot;).asText());
                transaction.setIsuNm(node.get(&quot;ISU_NM&quot;).asText());
                transaction.setMktNm(node.get(&quot;MKT_NM&quot;).asText());
                transaction.setAccTrdVol(node.get(&quot;ACC_TRDVOL&quot;).asLong());
                transaction.setMktCap(node.get(&quot;MKTCAP&quot;).asLong());

                repository.save(transaction);
            }
            return &quot;🍩🍩🍩 KRX 데이터 저장 완료&quot;;
        }catch (Exception e) {
            e.printStackTrace();
            return &quot;💥💥💥 KRX 데이터 저장 실패!&quot;;
        }
    }
}</code></pre>
<p>&lt;TrenController.java&gt;</p>
<pre><code class="language-java">package com.example.personal_investor_trend.controller;

import com.example.personal_investor_trend.model.StockTransaction;
import com.example.personal_investor_trend.repository.StockTransactionRepository;
import com.example.personal_investor_trend.service.KrxService;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Map;

@RestController
@RequestMapping(&quot;/api/trend&quot;)
public class TrendController {

   private final KrxService krxService;
   private final StockTransactionRepository repository;

   public TrendController(KrxService krxService, StockTransactionRepository repository){
    this.krxService = krxService;
    this.repository = repository;
   }

   @PostMapping(&quot;/krx&quot;)
    public String getKrxData(@RequestBody Map&lt;String, String&gt; request) {
        return krxService.getKrxData(request.get(&quot;basDd&quot;));
    }

    @GetMapping(&quot;/stored-data&quot;)
    public List&lt;StockTransaction&gt; getStoredData(@RequestParam String basDd){
        return repository.findByBasDd(basDd);
    }
}</code></pre>
<ul>
<li>결과
<img src="https://velog.velcdn.com/images/baby_potato/post/7743ce2b-286f-43b3-b243-2d092efbe548/image.png" alt="">
<img src="https://velog.velcdn.com/images/baby_potato/post/2cc48a46-f72f-4e9c-b2ec-824c9975f861/image.png" alt="">
<img src="https://velog.velcdn.com/images/baby_potato/post/5103bd3e-603c-40cd-8d48-7fa2c44d5248/image.png" alt=""></li>
</ul>
<h3 id="부가-기능-개발">부가 기능 개발</h3>
<h3 id="1-최근-1주일1개월-개인-투자자-순매수-상위-종목-조회">1. 최근 1주일/1개월 개인 투자자 순매수 상위 종목 조회</h3>
<blockquote>
<p>엔드포인트 : /api/trend/top-buyers
HTTP 메서드 : GET
요청 파라미터 : period = week / month  (type:String)</p>
</blockquote>
<ul>
<li><p>&lt;TrendController.java&gt; - /top-buyers 추가</p>
<pre><code class="language-java">@GetMapping(&quot;/top-buyers&quot;)
  public List&lt;Map&lt;String, Object&gt;&gt; getTopBuyers(@RequestParam String period) {
      // 조회 기간 설정 (7일 or 30일)
      String startDate = period.equals(&quot;week&quot;) 
              ? LocalDate.now().minusDays(7).toString()
              : LocalDate.now().minusDays(30).toString();

      // JPA Repository 호출
      List&lt;Object[]&gt; results = repository.findTopBuyers(startDate);

      // 응답 데이터를 JSON 형태로 가공
      return results.stream().map(result -&gt; Map.of(
              &quot;종목코드&quot;, result[0],
              &quot;종목명&quot;, result[1],
              &quot;순매수량&quot;, result[2]
      )).toList();
  }</code></pre>
</li>
<li><p>&lt;StockTransactionRepository.java&gt; - 상위 10개 조회 및 findTopBuyers 정의</p>
<pre><code class="language-java">/ 최근 7일 또는 30일 동안 개인 투자자 순매수 상위 10개 조회
  @Query(&quot;SELECT s.isuCd, s.isuNm, SUM(s.accTrdVol) AS totalTrdVol &quot; +
         &quot;FROM StockTransaction s &quot; +
         &quot;WHERE s.basDd &gt;= :startDate &quot; +
         &quot;GROUP BY s.isuCd, s.isuNm &quot; +
         &quot;ORDER BY totalTrdVol DESC&quot;)

  List&lt;Object[]&gt; findTopBuyers(@Param(&quot;startDate&quot;) String startDate);</code></pre>
</li>
<li><p>결과
<img src="https://velog.velcdn.com/images/baby_potato/post/6b180f8f-0914-4b38-a821-4888efbd22ae/image.png" alt=""></p>
</li>
</ul>
<h3 id="2-기관--외국인-투자자와-비교하여-매매-동향-분석">2. 기관 &amp; 외국인 투자자와 비교하여 매매 동향 분석</h3>
<blockquote>
<ul>
<li>최근 7일 / 30일 기준으로 동안 개인 vs 기관 vs 외국인의 매수/매도 흐름을 비교
엔드포인트 : /api/trend/comparison
HTTP 메서드 : GET
요청 파라미터 : period = week / month  (type:String)</li>
</ul>
</blockquote>
<ul>
<li><p>기관(instTrdVol) &amp; 외국인(foreignTrdVol) 거래량 컬럼 생성</p>
<pre><code class="language-java">private Long instTrdVol; // 기관 거래량 
private Long foreignTrdVol; // 외국인 거래량 </code></pre>
</li>
<li><p>기관(instTrdVol) &amp; 외국인(foreignTrdVol) 거래량 저장</p>
<pre><code class="language-java">transaction.setInstTrdVol(node.get(&quot;INST_TRDVOL&quot;).asLong()); 
transaction.setForeignTrdVol(node.get(&quot;FOREIGN_TRDVOL&quot;).asLong()); </code></pre>
</li>
<li><p>기관 &amp; 외국인과 비교하여 매매 동향 조회하는 쿼리 생성</p>
<pre><code class="language-java">@Query(&quot;SELECT s.isuCd, s.isuNm, SUM(s.accTrdVol) AS 개인, SUM(s.instTrdVol) AS 기관, SUM(s.foreignTrdVol) AS 외국인 &quot; +
     &quot;FROM StockTransaction s &quot; +
     &quot;WHERE s.basDd &gt;= :startDate &quot; +
     &quot;GROUP BY s.isuCd, s.isuNm&quot;)
List&lt;Object[]&gt; compareInvestorTrends(@Param(&quot;startDate&quot;) String startDate);</code></pre>
</li>
<li><p>엔드포인트 추가</p>
<pre><code class="language-java">@GetMapping(&quot;/comparison&quot;)
  public List&lt;Map&lt;String, Object&gt;&gt; getComparison(@RequestParam String period){
      String startDate = period.equals(&quot;week&quot;) 
              ? LocalDate.now().minusDays(7).toString()
              : LocalDate.now().minusDays(30).toString();

      List&lt;Object[]&gt; results = repository.compareInvestorTrends(startDate);

      return results.stream().map(result -&gt; Map.of(
          &quot;종목코드&quot;, result[0],
          &quot;종목명&quot;, result[1],
          &quot;개인 순매수량&quot;, result[2],
          &quot;기관 순매수량&quot;, result[3],
          &quot;외국인 순매수량&quot;, result[4]
      )).toList();
  }</code></pre>
</li>
<li><p>결과
<img src="https://velog.velcdn.com/images/baby_potato/post/8fc7e1db-4988-4276-b85c-5c4ce1e6e0f6/image.png" alt=""></p>
</li>
</ul>
<h3 id="3-ai-기반-투자자-관심-종목-추천">3. AI 기반 투자자 관심 종목 추천</h3>
<blockquote>
<p>최근 1개월 동안 개인 투자자가 많이 매수한 종목을 분석하여 비슷한 매매 패턴을 보이는 종목을 찾아 연관 종목 추천하는 기능 </p>
</blockquote>
<ul>
<li><p>단순 추천 로직을 사용해 기본적인 종목 추천 기능 개발</p>
</li>
<li><p>추후 AI 모델을 추가할 수 있도록 확장 가능하게 설계</p>
</li>
<li><p>최근 1개월 개인 순매수 상위10개 조회</p>
<pre><code class="language-java">@Query(&quot;SELECT s.isuCd, s.isuNm, SUM(s.accTrdVol) AS totalTrdVol &quot; +
     &quot;FROM StockTransaction s &quot; +
     &quot;WHERE s.basDd &gt;= :startDate &quot; +
     &quot;GROUP BY s.isuCd, s.isuNm &quot; +
     &quot;ORDER BY totalTrdVol DESC&quot;)
      List&lt;Object[]&gt; findTopPersonalStocks(@Param(&quot;startDate&quot;) String startDate, Pageable pageable);</code></pre>
<ul>
<li>처음에 LIMIT 10 으로 쿼리 줬다니 ERROR -&gt; JPA/HQL 에서는 LIMIT 은 직접 사용이 불가, 대신 Spring Data JPA의 Pageable 사용</li>
</ul>
</li>
<li><p>RecommendationService 생성</p>
<pre><code class="language-java">package com.example.personal_investor_trend.service;
</code></pre>
</li>
</ul>
<p>import com.example.personal_investor_trend.repository.StockTransactionRepository;
import org.springframework.stereotype.Service;
import org.springframework.data.domain.PageRequest; </p>
<p>import java.time.LocalDate;
import java.util.*;</p>
<p>@Service
public class RecommendationService {</p>
<pre><code>private final StockTransactionRepository repository;

public RecommendationService(StockTransactionRepository repository) {
    this.repository = repository;
}

public List&lt;Map&lt;String, String&gt;&gt; getRecommendedStocks() {
    String startDate = LocalDate.now().minusDays(30).toString();

    List&lt;Object[]&gt; topStocks = repository.findTopPersonalStocks(startDate, PageRequest.of(0, 10));

    // 연관 종목 ex
    Map&lt;String, String&gt; similarStocks = Map.of(
        &quot;090710&quot;, &quot;056080&quot;,  
        &quot;027040&quot;, &quot;080220&quot;,  
        &quot;466100&quot;, &quot;056080&quot;,  
        &quot;452450&quot;, &quot;108860&quot;  
    );

    List&lt;Map&lt;String, String&gt;&gt; recommendations = new ArrayList&lt;&gt;();

    for (Object[] stock : topStocks) {
        String isuCd = (String) stock[0];
        String isuNm = (String) stock[1];

        if (similarStocks.containsKey(isuCd)) {
            recommendations.add(Map.of(
                &quot;추천 종목코드&quot;, similarStocks.get(isuCd),
                &quot;추천 종목명&quot;, getStockName(similarStocks.get(isuCd)),
                &quot;추천 이유&quot;, isuNm + &quot;과 비슷한 매매 패턴을 보임&quot;
            ));
        }
    }

    return recommendations;
}

private String getStockName(String isuCd) {
    // 종목 코드 → 종목명 매핑 (추후 DB 조회 가능)
    Map&lt;String, String&gt; stockNames = Map.of(
        &quot;090710&quot;, &quot;휴림로봇&quot;,
        &quot;056080&quot;, &quot;유진로봇&quot;,
        &quot;027040&quot;, &quot;서울전자통신&quot;,
        &quot;080220&quot;, &quot;제주반도체&quot;,
        &quot;466100&quot;, &quot;클로봇&quot;,
        &quot;108860&quot;, &quot;셀바스AI&quot;,
        &quot;452450&quot;, &quot;피아이이&quot;
    );
    return stockNames.getOrDefault(isuCd, &quot;알 수 없음&quot;);
}</code></pre><p>}</p>
<pre><code>* 엔드포인트 생성
```java
@GetMapping(&quot;/recommendations&quot;)
    public List&lt;Map&lt;String, String&gt;&gt; getRecommendations(){
        return RecommendationService.getRecommendedStocks();
    }</code></pre><ul>
<li>결과
<img src="https://velog.velcdn.com/images/baby_potato/post/5bc771c8-55b2-4331-9391-1cacc753cb2f/image.png" alt=""></li>
</ul>
<h3 id="4-관심종목-저장-및-조회">4. 관심종목 저장 및 조회</h3>
<blockquote>
<ul>
<li>사용자가 관심있는 종목을 선택해서 저장
엔드포인트 : /api/user/watchlist
HTTP 메서드 : POST
Req Body : {<pre><code>  &quot;userId&quot;: 1,
  &quot;stockCode&quot;: &quot;005930&quot;,
  &quot;stockName&quot;: &quot;삼성전자&quot;</code></pre>  }
Res : {&quot;message&quot;: &quot;삼성전자 (005930) 관심 종목으로 저장 완료!&quot;}    </li>
</ul>
</blockquote>
<ul>
<li>사용자 괌심종목 테이블 생성<pre><code class="language-java">package com.example.personal_investor_trend.model;
</code></pre>
</li>
</ul>
<p>import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;</p>
<p>@Entity
@Getter
@Setter
@NoArgsConstructor
@Table(name = &quot;user_watchlist&quot;)
public class UserWatchlist {</p>
<pre><code>@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private Long userId;  // 사용자 ID
private String stockCode;  // 종목 코드
private String stockName;  // 종목명</code></pre><p>}</p>
<pre><code>* Repository 생성
```java
package com.example.personal_investor_trend.repository;

import com.example.personal_investor_trend.model.UserWatchlist;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface UserWatchlistRepository extends JpaRepository&lt;UserWatchlist, Long&gt; {
    List&lt;UserWatchlist&gt; findByUserId(Long userId);
}</code></pre><ul>
<li>Service 생성<pre><code class="language-java">package com.example.personal_investor_trend.service;
</code></pre>
</li>
</ul>
<p>import com.example.personal_investor_trend.model.UserWatchlist;
import com.example.personal_investor_trend.repository.UserWatchlistRepository;
import org.springframework.stereotype.Service;</p>
<p>import java.util.List;</p>
<p>@Service
public class UserWatchlistService {</p>
<pre><code>private final UserWatchlistRepository repository;

public UserWatchlistService(UserWatchlistRepository repository) {
    this.repository = repository;
}

public UserWatchlist addWatchlist(Long userId, String stockCode, String stockName) {
    UserWatchlist watchlist = new UserWatchlist();
    watchlist.setUserId(userId);
    watchlist.setStockCode(stockCode);
    watchlist.setStockName(stockName);
    return repository.save(watchlist);
}

public List&lt;UserWatchlist&gt; getUserWatchlist(Long userId) {
    return repository.findByUserId(userId);
}</code></pre><p>}</p>
<pre><code>*  Controller 생성
```java
package com.example.personal_investor_trend.controller;

import com.example.personal_investor_trend.model.UserWatchlist;
import com.example.personal_investor_trend.service.UserWatchlistService;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Map;

@RestController
@RequestMapping(&quot;/api/user&quot;)
public class UserController {

    private final UserWatchlistService watchlistService;

    public UserController(UserWatchlistService watchlistService) {
        this.watchlistService = watchlistService;
    }

    // 관심 종목 저장 (POST)
    @PostMapping(&quot;/watchlist&quot;)
    public Map&lt;String, String&gt; addWatchlist(@RequestBody Map&lt;String, String&gt; request) {
        Long userId = Long.parseLong(request.get(&quot;userId&quot;));
        String stockCode = request.get(&quot;stockCode&quot;);
        String stockName = request.get(&quot;stockName&quot;);

        watchlistService.addWatchlist(userId, stockCode, stockName);
        return Map.of(&quot;message&quot;, stockName + &quot; (&quot; + stockCode + &quot;) 관심 종목으로 저장 완료!&quot;);
    }

    // 사용자의 관심 종목 조회 (GET)
    @GetMapping(&quot;/watchlist&quot;)
    public List&lt;UserWatchlist&gt; getUserWatchlist(@RequestParam Long userId) {
        return watchlistService.getUserWatchlist(userId);
    }
}
</code></pre><ul>
<li><p>결과</p>
<ul>
<li>관심종목 저장
<img src="https://velog.velcdn.com/images/baby_potato/post/4beb4b51-163c-4807-a7f6-091c39a36f65/image.png" alt=""></li>
</ul>
</li>
</ul>
<pre><code>* 데이터 적재
![](https://velog.velcdn.com/images/baby_potato/post/fcaa9836-eed0-4ca2-872c-55a7f7a94220/image.png)

* 관심종목 조회
![](https://velog.velcdn.com/images/baby_potato/post/8950ad4f-bcaa-40d6-89b9-e8967585513e/image.png)</code></pre><h3 id="5-사용자의-관심-종목-기반-뉴스-크롤링---웹크롤링-설정-필요">5. 사용자의 관심 종목 기반 뉴스 크롤링 - <em><strong>웹크롤링 설정 필요</strong></em></h3>
<blockquote>
<ul>
<li>사용자의 관심 종목(user_watchlist 테이블)에서 stockCode 목록을 가져와 뉴스 크롤링
엔드포인트 : 
HTTP 메서드 : POST</li>
</ul>
</blockquote>
<ul>
<li>repository<pre><code class="language-java">List&lt;UserWatchlist&gt; findStockCodeByUserId(Long userId);</code></pre>
</li>
<li>&lt;pom.xml&gt; - dependency 추가<pre><code class="language-xml">&lt;dependency&gt;
&lt;groupId&gt;org.jsoup&lt;/groupId&gt;
&lt;artifactId&gt;jsoup&lt;/artifactId&gt;
&lt;version&gt;1.16.1&lt;/version&gt;
&lt;/dependency&gt;</code></pre>
</li>
<li>&lt;NewsService.java&gt;<pre><code class="language-java">package com.example.personal_investor_trend.service;
</code></pre>
</li>
</ul>
<p>import com.example.personal_investor_trend.model.UserWatchList;
import com.example.personal_investor_trend.repository.UserWatchListRepository;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.stereotype.Service;</p>
<p>import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;</p>
<p>@Service
public class NewsService {</p>
<pre><code>private final UserWatchListRepository watchlistRepository;

public NewsService(UserWatchListRepository watchlistRepository) {
    this.watchlistRepository = watchlistRepository;
}

public List&lt;Map&lt;String, Object&gt;&gt; getNewsForUser(Long userId) {
    List&lt;UserWatchList&gt; watchlist = watchlistRepository.findByUserId(userId);
    List&lt;Map&lt;String, Object&gt;&gt; newsData = new ArrayList&lt;&gt;();

    for (UserWatchList stock : watchlist) {
        String stockCode = stock.getStockCode();
        String stockName = stock.getStockName();

        List&lt;Map&lt;String, String&gt;&gt; newsList = getStockNews(stockName);

        newsData.add(Map.of(&quot;종목코드&quot;, stockCode, &quot;종목명&quot;, stockName, &quot;관련 뉴스&quot;, newsList));
    }

    return newsData;
}

// 크롤링 메서드
private List&lt;Map&lt;String, String&gt;&gt; getStockNews(String stockName) {
    List&lt;Map&lt;String, String&gt;&gt; newsList = new ArrayList&lt;&gt;();
    try {
        String encodedQuery = URLEncoder.encode(stockName, StandardCharsets.UTF_8);
        //네이버 뉴스 
        String url = &quot;https://search.naver.com/search.naver?where=news&amp;ie=utf8&amp;sm=nws_hty&amp;query=&quot; + encodedQuery;

        Document doc = Jsoup.connect(url).get();
        Elements newsElements = doc.select(&quot;.list_news .news_area&quot;); 

        for (Element news : newsElements) {
            Element titleElement = news.selectFirst(&quot;.news_tit&quot;); //제목
            if (titleElement == null) continue;

            String title = titleElement.text();
            String link = titleElement.attr(&quot;href&quot;);

            newsList.add(Map.of(&quot;제목&quot;, title, &quot;링크&quot;, link));

            if (newsList.size() &gt;= 5) break; // 최대 5개 
        }
    } catch (IOException e) {
        e.printStackTrace();
    }

    return newsList;
}</code></pre><p>}</p>
<pre><code>
* &lt;NewsController.java&gt;
```java
package com.example.personal_investor_trend.controller;

import com.example.personal_investor_trend.service.NewsService;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Map;

@RestController
@RequestMapping(&quot;/api/user&quot;)
public class NewsController {

    private final NewsService newsService;

    public NewsController(NewsService newsService) {
        this.newsService = newsService;
    }

    // 사용자 관심 종목 뉴스 조회 
    @PostMapping(&quot;/news&quot;)
    public List&lt;Map&lt;String, Object&gt;&gt; getUserNews(@RequestBody Map&lt;String, String&gt; request) {
        Long userId = Long.parseLong(request.get(&quot;userId&quot;));
        return newsService.getNewsForUser(userId);
    }
}</code></pre><ul>
<li>결과
<img src="https://velog.velcdn.com/images/baby_potato/post/9aa52b1b-bdb5-4450-aa9a-b4e8c410db0e/image.png" alt=""></li>
</ul>
<h2 id="끝">끝</h2>
<ul>
<li>한국거래소(KRX)의 OpenAPI데이터를 활용해서 REST API 호출</li>
<li>JSOUP -&gt; 실시간 웹 크롤링 사용</li>
</ul>
<p>위의 두가지를 제일 신경 쓴 간단한 프로젝트였다!</p>
<p>추후에 딥하게 들어간다면 실시간으로 주식 시세나 알림을 사용자에게 제공하고 AI기반으로 투자를 추천해준다거나 더 많은 공공데이터를 함께 사용해서 종목에 대한 분석을 강화할 수 있지 않을까?!</p>
<p><img src="https://velog.velcdn.com/images/baby_potato/post/f6484716-e3e8-49c0-b8f8-3093ebb67b3d/image.jfif" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[토큰 기반 권한별 기능 제한 아르켜줄게]]></title>
            <link>https://velog.io/@baby_potato/%ED%86%A0%ED%81%B0-%EA%B8%B0%EB%B0%98-%EA%B6%8C%ED%95%9C%EB%B3%84-%EA%B8%B0%EB%8A%A5-%EC%A0%9C%ED%95%9C</link>
            <guid>https://velog.io/@baby_potato/%ED%86%A0%ED%81%B0-%EA%B8%B0%EB%B0%98-%EA%B6%8C%ED%95%9C%EB%B3%84-%EA%B8%B0%EB%8A%A5-%EC%A0%9C%ED%95%9C</guid>
            <pubDate>Fri, 07 Feb 2025 07:03:51 GMT</pubDate>
            <description><![CDATA[<h2 id="history">History</h2>
<p>개발 중인 프로젝트에서 로그인 권한별로 기능을 제한하기로 했다.(당연한 기능이긴 함)</p>
<p>FastAPI를 사용하여 토큰 기반으로 사용자의 권한을 체크하고 특정 권한만 삭제, 수정 등이 가능하도록 구현하기로 했다.</p>
<h2 id="solve">Solve</h2>
<h3 id="1-클라이언트---서버">1. 클라이언트 -&gt; 서버</h3>
<ul>
<li>사용자는 로그인 후 서버에서 발급한 token 을 쿠키에 저장하고 
API요청 시 쿠키를 포함하여 요청을 보냄</li>
</ul>
<h3 id="2-서버--토큰인증">2. 서버-&gt; 토큰인증</h3>
<ul>
<li>request.cookies.get(&quot;token&quot;) 을 사용해 쿠키에서 토큰을 가져옴.</li>
<li>util_authentication.authentication(&quot;/clsf/delete&quot;, token) 을 호출해 토큰을 검증하고 사용자 정보(userId, role)를 추출.
<img src="https://velog.velcdn.com/images/baby_potato/post/c65f7178-b95b-40eb-9e64-eae9ef61e3f7/image.png" alt=""></li>
</ul>
<h3 id="3-role-확인-후-기능-제한">3. Role 확인 후 기능 제한</h3>
<ul>
<li>&lt;util_authentication.py&gt;
-&gt; JWT토큰을 디코딩해서 사용자 정보 추출 코드
-&gt; JWT 토큰을 HS256 알고리즘을 사용하여 검증<pre><code class="language-python">import os
from typing import Any, Dict, Optional, Union
import jwt
from fastapi_base.web.response_base import Result, SodaflowResponseBase
</code></pre>
</li>
</ul>
<h1 id="env_email-을-가져오는데-없다면-aaagmailcom로">ENV_EMAIl 을 가져오는데 없다면 <a href="mailto:aaa@gmail.com">aaa@gmail.com</a>로</h1>
<p>ENV_EMAIl = os.getenv(&#39;ENV_EMAIl&#39;, &#39;aaa@gmail.com&#39;)
ENV_EMAIL_ENCODE = ENV_EMAIl.encode()
#util_authentication.ENV_EMAIl.encode()
ALGORITHM = &quot;HS256&quot;</p>
<p>checkedRole = [
    { &#39;endpoint&#39;: &quot;/clsf/delete/&quot;, &#39;roles&#39;: [&#39;000&#39;, &#39;001&#39;, &#39;002&#39;] },
]</p>
<p>def authentication(
    endpoint: str, token: Optional[str]
) -&gt; Union[SodaflowResponseBase[None], SodaflowResponseBase[Dict[str, Any]]]:
    failed_result = SodaflowResponseBase[None](
        data=None, result=Result(code=-1, message=&quot;Permission is denied.&quot;)
    )
    if token is None:
        return failed_result</p>
<pre><code>data: Dict[str, Any] = jwt.decode(
    token, ENV_EMAIL_ENCODE, algorithms=[ALGORITHM]
)

# checkedRole 리스트를 순회하여 endpoint 찾기
for role in checkedRole:
    if role[&quot;endpoint&quot;] == endpoint:
        # endpoint가 일치하면 roles 배열 안에 data[&#39;role&#39;]이 있는지 확인
        if data[&quot;role&quot;] in role[&quot;roles&quot;]:
            return SodaflowResponseBase(
                data=data,
                result=Result(
                    code=0, message=&quot;Authentication successful.&quot;
                ),
            )

return failed_result</code></pre><pre><code>
### 4. 제한 조건 만족 시 기능 실행
* delete_clsf_service() 호출
![](https://velog.velcdn.com/images/baby_potato/post/b60061c9-313e-4a13-b7c3-0768b5ada3f9/image.png)
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[2.7~8.Java 자바 메모리 구조와 static, final]]></title>
            <link>https://velog.io/@baby_potato/2.78.Java-%EC%9E%90%EB%B0%94-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B5%AC%EC%A1%B0%EC%99%80-static-final</link>
            <guid>https://velog.io/@baby_potato/2.78.Java-%EC%9E%90%EB%B0%94-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B5%AC%EC%A1%B0%EC%99%80-static-final</guid>
            <pubDate>Thu, 16 Jan 2025 09:07:36 GMT</pubDate>
            <description><![CDATA[<h1 id="자바-메모리-구조와-static">자바 메모리 구조와 static</h1>
<h2 id="자바-메모리-구조">자바 메모리 구조</h2>
<p>자바의 메모리 구조는 크게 메서드 영역, 스택 영역, 힙 영역 3개로 나눌 수 있다.
<img src="https://velog.velcdn.com/images/baby_potato/post/9530cea2-bc87-482c-8602-dac5eba9941c/image.png" alt=""></p>
<ul>
<li>메서드 영역 : 클래스정보를 보관 (붕어빵 틀)</li>
<li>스택 영역 : 실제 프로그램이 실행되는 영역, 메서드를 실행할 때마다 하나씩 쌓인다.</li>
<li>힙 영역 : 객체(인스턴스)가 생성되는 영역, new 명령어를 사용(생성된 붕어빵이 존재하는 공간)</li>
</ul>
<p><img src="https://velog.velcdn.com/images/baby_potato/post/4c55264d-5545-45de-a40b-92d2c6e08217/image.png" alt=""></p>
<ul>
<li>메서드 영역 : 메서드 영역은 프로그램을 실행하는데 필요한 공통 데이터를 관리한다. 이 영역은
프로그램의 모든 영역에서 공유한다.<ul>
<li>클래스 정보: 클래스의 실행 코드(바이트 코드), 필드, 메서드와 생성자 코드등 모든 실행코드가 존재</li>
<li>static 영역: static 변수들을 보관.</li>
<li>런타임 상수 풀: 프로그램을 실행하는데 필요한 공통 리터럴 상수를 보관. 예를 들어서 프로그램에 &quot;hello&quot; 라는 리터럴 문자가 있으면 이런 문자를 공통으로 묶어서 관리한다. 이 외에도 프로그램을 효율적으로 관리하기 위한 상수들을 관리한다. (참고로 문자열을 다루는 문자열 풀은 자바 7부터 힙 영역으로 이동했다.)</li>
</ul>
</li>
<li>스택 영역 : 자바 실행 시, 하나의 실행 스택이 생성된다. 각 스택 프레임은 지역 변수, 중간 연산 결과, 메서드 호출 정보 등을 포함한다.<ul>
<li>스택 프레임: 스택 영역에 쌓이는 네모 박스가 하나의 스택 프레임이다. 메서드를 호출할 때 마다 하나의 스택 프레임이 쌓이고, 메서드가 종료되면 해당 스택 프레임이 제거됨.</li>
</ul>
</li>
<li>힙 영역 : 객체(인스턴스)와 배열이 생성되는 영역이다. 가비지 컬렉션(GC)이 이루어지는 주요 영역이며, 더 이상 참조되지 않는 객체는 GC에 의해 제거된다.</li>
</ul>
<h2 id="스택과-큐-자료구조">스택과 큐 자료구조</h2>
<p><img src="https://velog.velcdn.com/images/baby_potato/post/fcac506f-e253-4828-9ee4-6104b0e79e65/image.png" alt=""></p>
<ul>
<li>후입 선출(LIFO, Last In First Out) : 여기서 가장 마지막에 넣은 3번이 가장 먼저 나온다. 이렇게 나중에 넣은 것이 가장 먼저 나오는 것을 후입 선출이라 하고, 이런 자료 구조를 <strong>스택</strong>이라 한다.
<img src="https://velog.velcdn.com/images/baby_potato/post/5902e928-4de9-45ab-96dc-ae2924853faa/image.png" alt=""></li>
<li>선입 선출(FIFO, First In First Out) : 후입 선출과 반대로 가장 먼저 넣은 것이 가장 먼저 나오는 것을 선입 선출이라 한다. 이런 자료 구조를 <strong>큐(Queue)</strong>라고 한다.</li>
</ul>
<h2 id="스택영역">스택영역</h2>
<p>&lt;JavaMemoryMain1.java&gt;</p>
<pre><code class="language-java">package memory;

public class JavaMemoryMain1 {
    public static void main(String[] args) {
        System.out.println(&quot;main start&quot;);
        method1(10);
        System.out.println(&quot;main end&quot;);
    }
    static void method1(int m1){
        System.out.println(&quot;method1 start&quot;);
        int cal = m1 * 2 ;
        method2(cal);
        System.out.println(&quot;method1 end&quot;);
    }
    static void method2(int m2){
        System.out.println(&quot;method2 start&quot;);
        System.out.println(&quot;method2 end&quot;);
    }

}</code></pre>
<p><img src="https://velog.velcdn.com/images/baby_potato/post/7eaf3cb8-422b-484a-babd-0670cbf2490f/image.png" alt="">
-&gt; <strong>스택</strong> 구조</p>
<blockquote>
<ul>
<li>자바는 스택 영역을 사용해서 메서드 호출과 지역 변수(매개변수 포함)를 관리한다.</li>
</ul>
</blockquote>
<ul>
<li>메서드를 계속 호출하면 스택 프레임이 계속 쌓인다.</li>
<li>지역 변수(매개변수 포함)는 스택 영역에서 관리한다.</li>
<li>스택 프레임이 종료되면 지역 변수도 함께 제거된다.</li>
<li>스택 프레임이 모두 제거되면 프로그램도 종료된다.</li>
</ul>
<h2 id="스택-영역과-힙-영역">스택 영역과 힙 영역</h2>
<p>&lt;Data.java&gt;</p>
<pre><code class="language-java">package memory;

public class Data {
    private int value;

    public Data(int value){
        this.value = value;
    }
    public int getValue(){
        return value;
    }
}</code></pre>
<p>&lt;JavaMemoryMain2.java&gt;</p>
<pre><code class="language-java">package memory;

public class JavaMemoryMain2 {
    public static void main(String[] args) {
        System.out.println(&quot;main start&quot;);
        method1();
        System.out.println(&quot;main end&quot;);
    }
    static void method1(){
        System.out.println(&quot;method1 start&quot;);
        Data data1 = new Data(10);
        method2(data1);
        System.out.println(&quot;method1 end&quot;);
    }
    static void method2(Data data2){
        System.out.println(&quot;method2 start&quot;);
        System.out.println(&quot;data.value = &quot;+ data2.getValue());
        System.out.println(&quot;method2 end&quot;);
    }

}</code></pre>
<p><img src="https://velog.velcdn.com/images/baby_potato/post/a6622e57-5108-4d21-a3b9-67286d0bc862/image.png" alt=""></p>
<ul>
<li>method1에서 Data 클래스의 인스턴스 생성</li>
<li>method1() 에서 method2() 를 호출할 때 매개변수에 Data 인스턴스의 참조값을 전달
<img src="https://velog.velcdn.com/images/baby_potato/post/b108f78b-6b7c-45f4-81f0-4ab9cc254606/image.png" alt=""></li>
<li>method1() 은 지역 변수로 Data data1 을 가지고 있다. 이 지역 변수도 스택 프레임에 포함된다.</li>
<li>method1() 은 new Data(10) 를 사용해서 힙 영역에 Data 인스턴스를 생성한다. 그리고 참조값을 data1에 보관한다.
<img src="https://velog.velcdn.com/images/baby_potato/post/78ce57c9-10cc-433b-a43d-4b713f7011f5/image.png" alt=""></li>
<li>method1() 은 method2() 를 호출하면서 Data data2 매개변수에 x001 참조값을 넘긴다.</li>
<li>이제 method1() 에 있는 data1 과 method2() 에 있는 data2 지역 변수(매개변수 포함)는 둘다 같은 x001 인스턴스를 참조한다.</li>
</ul>
<blockquote>
<p>지역 변수는 스택 영역에, 객체(인스턴스)는 힙 영역에 관리되는 것을 확인했다. 이제 나머지 하나가 남았다. 바로 메서드 영역이다. 메서드 영역이 관리하는 변수도 있다. 이것을 이해하기 위해서는 먼저 static 키워드를 알아야 한다. static 키워드는 메서드 영역과 밀접한 연관이 있다.</p>
</blockquote>
<h2 id="static-변수1">static 변수1</h2>
<p>생성된 데이터들의 갯수세기</p>
<h3 id="인스턴스-내부-변수에-카운트-저장">인스턴스 내부 변수에 카운트 저장</h3>
<p>&lt;Data1.java&gt;</p>
<pre><code class="language-java">package static1;

public class Data1 {
    public String name;
    public int count;

    public Data1(String name){
        this.name = name;
        count++;
    }
}</code></pre>
<p>&lt;DataCountMain1.java&gt;</p>
<pre><code class="language-java">package static1;

public class DataCountMain1 {
    public static void main(String[] args) {
        Data1 data1 = new Data1(&quot;A&quot;);
        System.out.println(&quot;A count=&quot; + data1.count);

        Data1 data2 = new Data1(&quot;B&quot;);
        System.out.println(&quot;B count=&quot; + data2.count);

        Data1 data3 = new Data1(&quot;C&quot;);
        System.out.println(&quot;C count=&quot; + data3.count);
    }
}</code></pre>
<p><img src="https://velog.velcdn.com/images/baby_potato/post/3845042a-1cef-4888-8643-c41079196158/image.png" alt="">
-&gt; 이 프로그램은 당연히 기대한 대로 작동하지 않는다. 객체를 생성할 때 마다 Data1 인스턴스는 새로 만들어진다. 그리고 인스턴스에 포함된 count 변수도 새로 만들어지기 때문이다.(count 는 계속 초기화된다.)
=&gt;인스턴스에 사용되는 멤버 변수 count 값은 인스턴스끼리 서로 공유되지 않는다. 따라서 원하는 답을 구할 수 없다. 이 문제를 해결하려면 <strong>변수를 서로 공유</strong>해야 한다.</p>
<h2 id="외부-인스턴스에-카운트-저장">외부 인스턴스에 카운트 저장</h2>
<p>&lt;Counter.java&gt;</p>
<pre><code class="language-java">package static1;

public class Counter {
    public int count;
}</code></pre>
<p>&lt;Data2.java&gt;</p>
<pre><code class="language-java">package static1;

public class Data2 {
    public String name;

    public Data2(String name, Counter counter){
        this.name=name;
        counter.count++;
    }
}</code></pre>
<p>&lt;DataCountMain2.java&gt;</p>
<pre><code class="language-java">package static1;

public class DataCountMain2 {
    public static void main(String[] args) {
        Counter counter = new Counter();
        Data2 data1 = new Data2(&quot;A&quot;,counter);
        System.out.println(&quot;A count=&quot;+ counter.count);

        Data2 data2 = new Data2(&quot;B&quot;,counter);
        System.out.println(&quot;B count=&quot;+ counter.count);

        Data2 data3 = new Data2(&quot;C&quot;,counter);
        System.out.println(&quot;C count=&quot;+ counter.count);
    }
}</code></pre>
<p><img src="https://velog.velcdn.com/images/baby_potato/post/0b63c7f0-e7fb-485d-b232-c5657fb926ea/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/baby_potato/post/684e941a-d98b-4149-9b66-676072b7a303/image.png" alt="">
-&gt; Data2의 인스턴스는 3개가 생성
=&gt; 하지만 Data2 클래스와 관련된 일인데, Counter 라는 별도의 클래스를 추가로 사용해야 하고, 생성자 매개변수 추가로 생성자와 생성자 호출부분이 복잡해진다.
=&gt; 외부의 도움을 받지 않는 방법?(Counter 메서드를 따로 만들지 않는 법?) =static</p>
<h2 id="static-변수2">static 변수2</h2>
<p>특정 클래스에서 공용으로 함께 사용할 수 있는 변수를 만들 수 있다면 편리할 것이다.
static 키워드를 사용하면 공용으로 함께 사용하는 변수를 만들 수 있다.
&lt;Data3.java&gt;</p>
<pre><code class="language-java">package static1;

public class Data3 {
    public String name;
    public static int count;

    public Data3(String name){
        this.name = name;
        count++;
    }
}</code></pre>
<blockquote>
<p>멤버 변수에 static 을 붙이면 static변수, 정적변수 또는 클래스변수라 한다.</p>
</blockquote>
<ul>
<li>객체가 생성되면 생성자에서 정적 변수 count 의 값을 하나 증가시킨다.</li>
</ul>
<p>&lt;DataCountMain3.java&gt;</p>
<pre><code class="language-java">package static1;

public class DataCountMain3 {
    public static void main(String[] args) {
        Data3 data1 = new Data3(&quot;A&quot;);
        System.out.println(&quot;A count=&quot;+ Data3.count);

        Data3 data2 = new Data3(&quot;B&quot;);
        System.out.println(&quot;B count=&quot;+ Data3.count);

        Data3 data3 = new Data3(&quot;C&quot;);
        System.out.println(&quot;C count=&quot;+ Data3.count);
    }
}</code></pre>
<p>-&gt; Data3.count 는 마치 클래스에 직접 접근하는 것 처럼 느껴짐
<img src="https://velog.velcdn.com/images/baby_potato/post/25999d50-9c23-4d74-a22d-ea97f6e6a976/image.png" alt=""></p>
<ul>
<li>static 이 붙은 멤버 변수는 <strong>메서드 영역</strong>에서 관리한다.
  static 이 붙은 멤버 변수 count 는 인스턴스 영역에 생성되지 않는다. 대신에 메서드 영역에서 이 변수를 관리한다.</li>
<li>Data3(&quot;A&quot;) 인스턴스를 생성하면 생성자가 호출된다.</li>
<li>생성자에는 count++ 코드가 있다. count 는 static 이 붙은 정적 변수다. 정적 변수는 인스턴스 영역이 아니라 메서드 영역에서 관리한다. 따라서 이 경우 메서드 영역에 있는 count 의 값이 하나 증가된다.</li>
</ul>
<p>=&gt;static 변수는 쉽게 이야기해서 클래스인 붕어빵 틀이 특별히 관리하는 변수이다. 붕어빵 틀은 1개이므로 클래스 변수도 하나만 존재한다. 반면에 인스턴스 변수는 붕어빵인 인스턴스의 수 만큼 존재한다.</p>
<h2 id="static-변수-3">static 변수 3</h2>
<pre><code class="language-java">public class Data3 {
    public String name;
    public static int count;

    public Data3(String name){
        this.name = name;
        count++;
    }
}</code></pre>
<ul>
<li>name.count -&gt; 멤버변수(필드)</li>
</ul>
<p>멤버변수는 static의 유무로 두가지로 나뉨</p>
<ul>
<li>인스턴스 변수 : static이 붙지 않은 변수 예)name<ul>
<li>static 이 붙지 않은 멤버 변수는 인스턴스를 생성해야 사용할 수 있고, 인스턴스에 소속되어 있다. 따라서 인스턴스 변수라 한다.</li>
<li>인스턴스 변수는 인스턴스를 만들 때 마다 새로 만들어진다.</li>
</ul>
</li>
<li>클래스 변수 : static 이 붙은 멤버 변수, 예) count<ul>
<li>클래스 변수, 정적 변수, static 변수 등으로 부른다. 용어를 모두 사용하니 주의하자!</li>
<li>static 이 붙은 멤버 변수는 인스턴스와 무관하게 클래스에 바로 접근해서 사용할 수 있고, 클래스 자체에 소속되어 있다. 따라서 클래스 변수라 한다.</li>
<li>클래스 변수는 자바 프로그램을 시작할 때 딱 1개가 만들어진다. 인스턴스와는 다르게 보통 여러곳에서 공유하는 목적으로 사용된다.</li>
</ul>
</li>
</ul>
<h3 id="변수와-생명주기">변수와 생명주기</h3>
<ul>
<li>지역변수 : 스택영역에 생성, 메서드가 종료되면 스택 프레임이 제거되면서 지역변수도 같이 제거</li>
<li>인스턴스 변수 : 힙 영역에 생성, GC가 발생하기 전까지만 생존</li>
<li>클래스 변수 : 메서드 영역에 생성, JVM 이 종료되기 전까지 생존</li>
</ul>
<blockquote>
<p>static 은 정적변수다. 왜냐하면 인스턴스변수는 힙영역에서 동적으로 제거되고 생성되지만 static은 프로그램이 시작하면서 종료될때까지 존재하기 때문이다.</p>
</blockquote>
<h3 id="정적변수-접근법">정적변수 접근법</h3>
<p>static 변수는 클래스를 통해 바로 접근할 수도 있고, 인스턴스를 통해서도 접근할 수 있다.</p>
<p>&lt;DataCountMain3.java&gt;</p>
<pre><code class="language-java">package static1;

public class DataCountMain3 {
    public static void main(String[] args) {
        Data3 data1 = new Data3(&quot;A&quot;);
        System.out.println(&quot;A count=&quot;+ Data3.count);

        Data3 data2 = new Data3(&quot;B&quot;);
        System.out.println(&quot;B count=&quot;+ Data3.count);

        Data3 data3 = new Data3(&quot;C&quot;);
        System.out.println(&quot;C count=&quot;+ Data3.count);

        //추가
        //인스턴스를 통한 접근
        Data3 data4 = new Data3(&quot;D&quot;);
        System.out.println(data4.count);

        //클래스를 통한 접근
        System.out.println(Data3.count);
    }
}</code></pre>
<p>인스턴스를 통한 접근은 권장하지 않는다.
왜냐? 가져다 쓰는 입장에서 data4.count는 인스턴스변수인가? 하는 의문이 들 수 있지만 Data3.count는 누가봐도 static 이네!가 가능해서..</p>
<h2 id="static-메서드1">static 메서드1</h2>
<h3 id="인스턴스-메서드">인스턴스 메서드</h3>
<p>&lt;DecoUtil1.java&gt;</p>
<pre><code class="language-java">package static2;

public class DecoUtil1 {
    public String deco(String str){ //deco 메서드는 멤버변수도 없고 단순히 기능만 제공
        String result = &quot;*&quot; + str + &quot;*&quot;;
        return result;
    }
}</code></pre>
<p>&lt;DecoMain1.java&gt;</p>
<pre><code class="language-java">package static2;

public class DecoMain1 {
    public static void main(String[] args) {
        String s = &quot;hello JAVA&quot;;

        DecoUtil1 utils = new DecoUtil1();
        String deco = utils.deco(s);

        System.out.println(&quot;before: &quot;+s);
        System.out.println(&quot;after: &quot;+deco);

    }
}</code></pre>
<p>위의 코드에서 deco() 메서드는 멤버변수도 없고 기능만 제공하는 메서드이다. 그런데 굳이 Main 코드에서 정의하고 호출까지 해야할까?</p>
<h3 id="static-메서드">static 메서드</h3>
<p>&lt;DecoUtil2.java&gt;</p>
<pre><code class="language-java">package static2;

public class DecoUtil2 {
    public static String deco(String str){ //정적 메서드 : 인스턴스 생성 없이 클래스로 바로 호출 가능
        String result = &quot;*&quot; + str + &quot;*&quot;;
        return result;
    }
}</code></pre>
<p>&lt;DecoMain2.java&gt;</p>
<pre><code class="language-java">package static2;

public class DecoMain2 {
    public static void main(String[] args) {
        String s = &quot;hello JAVA&quot;;

        String deco = DecoUtil2.deco(s); //static 때문에 바로 호출 가능

        System.out.println(&quot;before: &quot;+s);
        System.out.println(&quot;after: &quot;+deco);

    }
}</code></pre>
<p>메서드에 static이 붙음으로써 정적메서드가 된다. 이렇게 하면 인스턴스 생성없이 클래스명으로 바로 호출이 가능하다. -&gt; 불필요한 객체생성이 없어짐</p>
<ul>
<li>static이 붙은 메서드를 <strong>정적 메서드</strong> or <strong>클래스메서드</strong>라 한다.</li>
<li>static이 붙지 않은 메서드는 <strong>인스턴스 메서드</strong></li>
</ul>
<h2 id="static메서드2">static메서드2</h2>
<h3 id="정적메서드-사용법">정적메서드 사용법</h3>
<ul>
<li>static은 static만 사용가능<ul>
<li>정적 메서드는 static이 붙은 정적메서드나 정적변수만 사용할 수 있다.</li>
<li>인스턴스는 불가능</li>
</ul>
</li>
<li>static은 어느곳에서도 호출이 가능</li>
</ul>
<p>&lt;DecoData.java&gt;</p>
<pre><code class="language-java">package static2;

public class DecoData {
    private int instanceValue;
    private static int staticValue;

    public static void staticCall(){
//        instanceValue ++; // 인스턴스 변수 접근 - 불가능
//        instanceMethod(); // 인스턴스 메서드 접근 - 불가능

        staticValue++; //정적 변수에 접근, 호출 가능
        staticMethod();

        //static 은 static만 호출가능
    }

    public void instanceCall(){
        instanceValue++;
        instanceMethod();

        staticValue++;
        staticMethod();

        //instance는 instance, static 호출가능
    }

    public static void staticCall(DecoData data){
        data.instanceValue++;
        data.instanceMethod();
        //static에서 instance 호출 가능하지만 여기 있는 data는 외부에 생긴 새로운 참조값
    }

    private void instanceMethod(){
        System.out.println(&quot;instanceValue=&quot; + instanceValue);
    }
    private static void staticMethod(){
        System.out.println(&quot;staticValue=&quot; + staticValue);
    }
}</code></pre>
<p>&lt;DecoDataMain.java&gt;</p>
<pre><code class="language-java">package static2;

public class DecoDataMain {
    public static void main(String[] args) {
        System.out.println(&quot;1.정적 호출&quot;);
        DecoData.staticCall();

        System.out.println(&quot;2.인스턴스 호출-인스턴스&quot;);
        DecoData decoData = new DecoData();
        decoData.instanceCall();

        System.out.println(&quot;3.인스턴스 호출-정적&quot;);
        DecoData.staticCall();

        System.out.println(&quot;3.정적 호출-외부참조&quot;);
        DecoData.staticCall(decoData);
    }
}</code></pre>
<ul>
<li>정적-&gt; 정적
  =&gt; 정적 메서드는 클래스 이름을 통해 바로 호출, 인스턴스처럼 참조값이 존재하지 않는다.
  BUT 객체의 참조값을 매개변수로 전달하면 인스턴스 호출 가능(외부에서 참조값을 생성하기때문에..ex.3.정적 호출-외부참조)</li>
<li>인스턴스 -&gt; 정적 &amp; 인스턴스</li>
</ul>
<h2 id="static메서드3">static메서드3</h2>
<h3 id="용어-정리">용어 정리</h3>
<ul>
<li>인스턴스 메서드: static이 붙지 않은 메서드</li>
<li>클래스 메서드: static이 붙은 메서드 - 정적메서드, static메서드 등으로 불림</li>
</ul>
<h4 id="정적-메서드-활용">정적 메서드 활용</h4>
<p>객체 생성이 필요 없이 메서드 호출만으로 필요한 기능을 수행할 때 사용, 유틸리티성 메서드</p>
<h3 id="정적메서드-접근법">정적메서드 접근법</h3>
<p><img src="https://velog.velcdn.com/images/baby_potato/post/0e759ff9-0468-4c59-a6d0-e31c8656c5c5/image.png" alt="">
둘다 결과적으로는 정적 메서드에 접근하지만 위의 것은 코드를 읽을 때 마치 인스턴스메서드에 접근하는 것처럼 오해 할 수 있음</p>
<h3 id="static-import">static import</h3>
<p>정적메서드를 자주 사용한다면 import 기능 사용
<img src="https://velog.velcdn.com/images/baby_potato/post/b2fe1236-1a84-47ae-b70e-3ee8ba062242/image.png" alt="">
Alt+Enter
<img src="https://velog.velcdn.com/images/baby_potato/post/33ef6369-e6d6-4908-949f-2abbaefc5d15/image.png" alt=""></p>
<pre><code class="language-java">import static static2.DecoData.*; // 모든 정적메서드에 적용</code></pre>
<h3 id="main-메서드는-정적메서드">main() 메서드는 정적메서드</h3>
<p>정적메서드인 main()메서드가 같은 클래스에서 호출하는 메서드도 정적메서드로 선언해야함.</p>
<pre><code class="language-java">package static2;

import oop1.ValueData;

public class ValueDataMain {
    public static void main(String[] args) { //static이기 때문에
        ValueData valueData = new ValueData();
        add(valueData);
    }
    static void add(ValueData valueData) {//static 으로 선언
        valueData.value++;
        System.out.println(&quot;숫자 증가 value=&quot; + valueData.value);
    }
}</code></pre>
<h1 id="final">final</h1>
<h2 id="final-변수와-상수1">final 변수와 상수1</h2>
<p>final은 이름 그대로 끝! 이라는 뜻, 변경이 불가능</p>
<pre><code class="language-java">package final1;

public class FinalLocalMain {
    public static void main(String[] args) {
        //final 지역변수
        final int data1;
        data1 = 10; //최초 한번만 할당 가능
//        data2 = 20; //컴파일 오류

        //final 지역변수2
        final int data2=10;
//        data2 =20;//컴파일 오류

        method(10);
    }
    static void method(final int parameter){
//        parameter = 10; //컴파일 오류 , 메서드 내에서 매개변수를 줄 수 없다.

    }
}</code></pre>
<ul>
<li>final 을 지역변수에 설정할 경우 최초 한번만 할당 가능</li>
<li>매개변수에 final이 붙으면 메서드 내부에서 매개변수의 값을 변경할 수 없다.</li>
</ul>
<pre><code class="language-java">package final1;

public class ConstructInit {
    final int value; // 이것만 하면 에러 남 -&gt; 아래처럼 생성자를 통해서 넣어야함

    public ConstructInit(int value){
        this.value = value;
    }
}</code></pre>
<ul>
<li>final을 필드에 사용하면 해당 필드는 생성자를 통해서 한번만 초기화 될 수 있다.</li>
</ul>
<pre><code class="language-java">package final1;

public class FieldInit {
    static final int CONST_VALUE = 10; //static final 대문자 -&gt; 관례

//    final int value = 10; // 초기 값을  경우에는 아래처럼 생성자를 통해서 할당이 불가
//    public FieldInit(int value){
//        this.value = value;
//    }

    final int value = 10;

}</code></pre>
<ul>
<li>final 필드를 필드에서 초기화하면 이미 값이 설정되었기 때문에 생성자를 통해서도 초기화 할 수 없다</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Http -> Https]]></title>
            <link>https://velog.io/@baby_potato/Http-Https</link>
            <guid>https://velog.io/@baby_potato/Http-Https</guid>
            <pubDate>Wed, 26 Jun 2024 05:20:30 GMT</pubDate>
            <description><![CDATA[<p>프로젝트 철수하는 주에 갑자기 고객사에서 인터페이스 테스트가 안된다는 연락을 받았다..</p>
<p>이유는.. 기존에 api 는 http 호출이었는데 
갑자기 https 호출을 한다는것 ..!</p>
<p>Https는 데이터 전송에 암호화가 걸리는 것이기 때문에 생각보다 고려해야할 게 너무 많아서 오래 걸렸다.. </p>
<p>간단하게 작업을 나열하자면</p>
<ol>
<li>SSL/TLS 인증서 발급 및 설정
 서버에 인증서 받기 -&gt; 이부분은 고객사와 서버관리팀에 요청해서 발급받고 설정할 수 있었다.</li>
<li>그 외 설정 <ul>
<li>포트 변경
  기존에 9100포트를 사용하였지만 보안 포트인 443 포트로 변경</li>
<li>kubenetes ingress<br>  도메인 설정
  서비스 연결
  SSL/TLS 설정 등</li>
<li>인터페이스 설정
SSL/TLS 설정
  port, ip 변경 
  소스 수정 등</li>
</ul>
</li>
</ol>
<h3 id="1-ssltls-인증서">1. SSL/TLS 인증서</h3>
<p>(.key 파일 없고 .pem 파일만 있을 때)
A. .key파일 생성</p>
<pre><code>openssl rsa -in key.pem -out cert.key</code></pre><p>B. 도메인에 대하여 인증서 등록</p>
<pre><code>openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ./tls/project-https-secret/tls.key -out ./tls/project-https-secret/tls.crt -subj 도메인명</code></pre><p>C. k8s 에서 인증서를 사용할 수 있도록 생성 </p>
<pre><code>kubectl -n project명 create secret tls twinreader-https --key ./tls/project-https-secret/tls.key --cert ./tls/project-https-secret/tls.crt</code></pre><p>D. nginx.yaml 에서 사용할 https port 선언(예시) 및 재기동</p>
<pre><code>spec:
  type: NodePort
  externalIPs:
    - IP입력
  ports:
    - name: http
      port: 8080
      protocol: TCP
      targetPort: 80
    - name: https
      port: 443
      protocol: TCP
      targetPort: 443</code></pre><p>E. ingress.yaml 설정
    가지고 있는 ingress 폴더에 하위에 생성한 secret 선언</p>
<h3 id="2-그-외-설정">2. 그 외 설정</h3>
<p>A. 인터페이스 SSL/TLS 설정
Keystore.p12생성
    tls 디렉토리 접근 후</p>
<pre><code>openssl pkcs12 -export -in /home/agilesoda/install/config/nginx/tls/twinreader-https-secret/tls.crt -inkey /home/agilesoda/install/config/nginx/tls/twinreader-https-secret/tls.key -out keystore.p12 -name tomcat</code></pre><p>생성된 keystore.p12를 인터페이스에 적용
&lt;application.properties&gt;</p>
<pre><code>server.ssl.key-store=/keystore.p12
server.ssl.key-store-type=PKCS12
server.ssl.key-store-password=패스워드</code></pre><p>그리고 나는 도커로 말아서 올릴거니까
Dockerfile 에</p>
<pre><code>ADD /install/keystore.p12 keystore.p12</code></pre><p>이거 추가</p>
<p>그리고그리고 Kubernetes 클러스터에 배포하기 위한 deployment.yaml 수정</p>
<pre><code>ports:
  - name: https
    protocol: TCP
    #appProtocol: https
    port: 443
    targetPort: 9000
  - name: http
    protocol: TCP
    port: 9100
    targetPort: 9100</code></pre><p>B. 개발 소스 수정
org.apache.coyote.http11.Http11NioProtocol&quot;을 사용하여 HTTP/1.1 NIO 프로토콜을 지정하고 커넥터가 사용하는 포트를 9100 으로 설정..</p>
<pre><code class="language-java">@Configuration
  public class TomcatConfig {

    @Bean
    public ServletWebServerFactory servletContainer() {
      TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
      tomcat.addAdditionalTomcatConnectors(createStandardConnector());
      return tomcat;
    }

    private Connector createStandardConnector() {
      Connector connector = new Connector(&quot;org.apache.coyote.http11.Http11NioProtocol&quot;);
      connector.setPort(9100);
      return connector;
    }
  }</code></pre>
<p>C. Ingress 설정
    클러스터 외부에서 내부 서비스로 HTTP 및 HTTPS 경로를 라우팅하는 규칙을 설정
    &lt;project-pension-ingress.yaml&gt;</p>
<pre><code>apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: project
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/ssl-redirect: &quot;false&quot;
    nginx.ingress.kubernetes.io/secure-backends: &quot;true&quot;
    nginx.ingress.kubernetes.io/backend-protocol: &quot;HTTPS&quot;
    #nginx.ingress.kubernetes.io/rewrite-target: /$2
    nginx.ingress.kubernetes.io/cors-allow-methods: &quot;GET,POST&quot;
spec:
  tls:
  - hosts:
    - &quot;도메인명&quot;
    secretName: twinreader-https
  rules:
  - host: &quot;도메인명&quot;
    http:
      paths:
      - path: /api/v1/aiocrSyncLoad(/|$)(.*)
        backend:
          serviceName: project
          servicePort: 443</code></pre><p> yaml 파일 수정후 반영(kubectl delete, apply..)</p>
<h2 id="이슈요약">이슈요약..</h2>
<p>처음엔 인증서만 받아서 보안포트 사용하면 되는거겟지~ 했는데
우선 인증서 발급과 반영에 대해 무지해서 도움을 많이 받았다.</p>
<p>인증서 적용 후 내 인터페이스의 POST 방식의 호출만 호출이 안되어서 그부분도 확인해보니 redirect 가 이상한 곳으로 가길래 ingress.yaml 의 rewrite 부분을 그냥 주석 해버렸더니 해결 되었었다.
해당 에러는</p>
<pre><code>Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method &#39;POST&#39; not supported]</code></pre><p>이렇게 생겼었고</p>
<pre><code>#logging.level.org.springframework=debug
#logging.level.org.springframework.web=debug
#logging.level.org.springframework.web.servlet.DispatcherServlet=DEBUG</code></pre><p>해당 설정값을 추가해 자세히 로그를 보니 알 수 있었다.</p>
<p>다음으론 인터페이스 내부였다..
인터페이스 내부에는 회사 제품을 호출하고 결과를 받아서 그결과를 보정한 후 전달하게 되는데
https로 들어와서 제품 호출하고 제품에서 http 로 다시 인터페이스를 콜백하는데 그 통신이 안되는것이었다..
그래서 인터페이스 deployment.yaml 을 보면 https 와 http 포트를 구분지어서 통신에따라 다르게 들어오게 처리 해줬다!
<img src="https://velog.velcdn.com/images/baby_potato/post/e2631939-b433-4e2f-a597-4f5d08c9a199/image.png" alt="">
이런식으로 port 가 두개!</p>
<h2 id="느낀점">느낀점</h2>
<p>그.. k8s 버전도 중요.. 꼭 버전에 맞는걸로 테스트 해보시길.. 버전마다 안되는 설정값이 있어서 이것도 애먹었다...</p>
<p>이제 앞으로는 처음 개발 시에 통신에 대해 정하고 시작하기로 다짐했다.
물론 당연히 데이터가 주고받는일에 단순히 내부통신이라고 보안을 생각못한 내잘 못도 있지만..
같이 테스트 하고 컨펌해줬으면서 갑자기 바꾸는 갈대 같은 고객사는... 어쩔 수 없지 뭐....</p>
<p>아직도 서버는 잘 모르겠지만...이렇게 틈틈이 접하다보면 괜찮아지겟지?</p>
<p><img src="https://velog.velcdn.com/images/baby_potato/post/8cf81add-0a28-435e-ad0c-1221fdc3b5e2/image.jpg" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[2.5~6.Java 패키지, 접근제어자]]></title>
            <link>https://velog.io/@baby_potato/2.56.Java-%ED%8C%A8%ED%82%A4%EC%A7%80</link>
            <guid>https://velog.io/@baby_potato/2.56.Java-%ED%8C%A8%ED%82%A4%EC%A7%80</guid>
            <pubDate>Mon, 13 May 2024 07:21:34 GMT</pubDate>
            <description><![CDATA[<h1 id="패키지">패키지</h1>
<h2 id="패키지-시작">패키지 시작</h2>
<p>기능이 점점 추가되어서 프로그램이 아주 커진다면?
-&gt; 매우 많은 클래스가 등장하면서 관련있는 기능들을 분류해서 관리해야한다. </p>
<blockquote>
<p>패키지 = 폴더, 디렉토리</p>
</blockquote>
<h2 id="패키지-사용">패키지 사용</h2>
<p>&lt;pack.Data.java&gt;</p>
<pre><code class="language-java">package pack;

public class Data {
    public Data(){
        System.out.println(&quot;패키지 pack Data 생성&quot;);
    }
}</code></pre>
<ul>
<li>패키지 사용 시 항상 코드 첫줄에 package packageName을 작성</li>
</ul>
<p>&lt;pack.a.User.java&gt;</p>
<pre><code class="language-java">package pack.a;

public class User {
    public User(){
        System.out.println(&quot;패키지 pack.a 회원 생성&quot;);
    }
}</code></pre>
<p><strong>참고</strong> : 생성자에 public 사용 -&gt; 다른 패키지에서 이 클래스의 생성자를 호출하려면 public을 사용해야한다. </p>
<p>&lt;PackageMain1.java&gt;</p>
<pre><code class="language-java">package pack;

public class PackageMain1 {
    public static void main(String[] args) {
        Data data = new Data();
        pack.a.User user = new pack.a.User();

    }
}</code></pre>
<p>&lt;결과&gt;
<img src="https://velog.velcdn.com/images/baby_potato/post/f2d43db6-5373-41ba-afba-89082029bebf/image.png" alt=""></p>
<h2 id="import">import</h2>
<p>&lt;PackageMain2.java&gt;</p>
<pre><code class="language-java">package pack;
import pack.a.User;

public class PackageMain2 {
    public static void main(String[] args) {
        Data data = new Data();
        User user = new User(); //import 사용으로 패키지명 생략가능

    }
}</code></pre>
<ul>
<li>코드 첫줄에는 package, 다음줄에는 import 사용</li>
<li>import 를 사용하면 다른 패키지에 있는 클래스를 가져와서 사용가능</li>
<li>특정 패키지의 모든 클래스를 포함하고 싶다면 &quot;*&quot;을 사용</li>
</ul>
<h3 id="패키지-사용-1">패키지(*) 사용</h3>
<pre><code class="language-java">package pack;
//import pack.a.User;
import pack.a.*; //pack.a의 모든 클래스 사용
public class PackageMain2 {
    public static void main(String[] args) {
        Data data = new Data();
        User user = new User(); //import 사용으로 패키지명 생략가능

    }
}</code></pre>
<h3 id="클래스-이름-중복">클래스 이름 중복</h3>
<p>&lt;pack.b.User.java&gt;</p>
<pre><code class="language-java">package pack.b;

public class User {
    public User() {
        System.out.println(&quot;패키지 pack.b 회원 생성&quot;);
    }
}</code></pre>
<p>&lt;PackageMain3.java&gt;</p>
<pre><code class="language-java">package pack;
//import pack.a.User;

import pack.a.User;

public class PackageMain3 {
    public static void main(String[] args) {
        User userA = new User(); //import 사용으로 패키지명 생략가능
        pack.b.User userB = new pack.b.User();
    }
}</code></pre>
<p>=&gt; 같은 이름의 클래스가 있다면 import는 둘중 하나만 선택할 수 있다. 이때는 자주 사용하는 클래스를 import 하고 나머지를 패키지를 포함한 전체 경로를 적어준다. 물론 둘다 전체 경로를 적어준다면 import 는 사용하지 않아도 됨</p>
<h2 id="패키지-규칙">패키지 규칙</h2>
<ul>
<li>패키지의 이름과 위치는 폴더(디렉토리) 위치와 같아야 한다. (필수)</li>
<li>패키지 이름은 모두 소문자를 사용한다. (관례)</li>
<li>패키지 이름의 앞 부분에는 일반적으로 회사의 도메인 이름을 거꾸로 사용한다. 예를 들어,
com.company.myapp 과 같이 사용한다. (관례)</li>
</ul>
<h3 id="패키지와-계층구조">패키지와 계층구조</h3>
<p>a , a.b , a.c 이렇게 3개의 패키지가 존재한다.
패키지가 계층 구조를 이루더라도 모든 패키지는 서로 다른 패키지이다.</p>
<h2 id="패키지-활용">패키지 활용</h2>
<p><strong>전체구조도</strong>
<img src="https://velog.velcdn.com/images/baby_potato/post/379208dd-3fec-44a1-a3ab-9f0dba8bc266/image.png" alt=""></p>
<p>&lt;com.helloshop.user.User.java&gt;</p>
<pre><code class="language-java">package com.helloshop.user;

public class User {
    String userId;
    String name;

}</code></pre>
<p>&lt;com.helloshop.user.UserService.java&gt;</p>
<pre><code class="language-java">package com.helloshop.user;

public class UserService {
}</code></pre>
<p>&lt;com.helloshop.product.Product.java&gt;</p>
<pre><code class="language-java">package com.helloshop.product;

public class Product {
    String productId;
    int price;
}</code></pre>
<p>&lt;com.helloshop.product.ProductService.java&gt;</p>
<pre><code class="language-java">package com.helloshop.product;

public class ProductService {
}</code></pre>
<p>&lt;com.helloshop.order.Order.java&gt;</p>
<pre><code class="language-java">package com.helloshop.order;
import com.helloshop.product.Product;
import com.helloshop.user.User;
public class Order {
    User user;
    Product product;

    public Order(User user, Product product){
        this.user = user;
        this.product=product;
    }
}</code></pre>
<p>=&gt; public Order(User user, Product product)에서 public : 접근제어자 
-&gt; public이 있어야 다른 패키지에서 order를 호출할 수 있다.
&lt;com.helloshop.order.OrderService.java&gt;</p>
<pre><code class="language-java">package com.helloshop.order;
import com.helloshop.product.Product;
import com.helloshop.user.User;

public class OrderService {
    public void order(){
        User user =new User();
        Product product = new Product();
        Order order = new Order(user, product);
    }
}</code></pre>
<h1 id="접근제어자">접근제어자</h1>
<p>자바는 public, private 같은 접근 제어자(access modifier) 를 제공한다.
접근 제어자를 사용하면 해당 클래스 외부에서 특정 필드나 메서드에 접근하는것을 허용하거나 제항 할 수 있다.</p>
<p>&lt;Speaker.java&gt;</p>
<pre><code class="language-java">package access;

public class Speaker {
    int volume;

    Speaker(int volume){
        this.volume = volume;
    }
    void volumeUp(){
        if(volume&gt;=100){
            System.out.println(&quot;음량을 증가할 수 없습니다. 최대 음량입니다.&quot;);
        }else {
            volume+=10;
            System.out.println(&quot;음량을 10 증가합니다.&quot;);
        }
    }
    void volumeDown(){
        volume-=10;
        System.out.println(&quot;음량을 10 감소합니다.&quot;);
    }
    void showVolume(){
        System.out.println(&quot;현재 음량: &quot;+ volume);
    }
}</code></pre>
<p>&lt;SpeakerMain.java&gt;</p>
<pre><code class="language-java">package access;

public class SpeakerMain {
    public static void main(String[] args) {
        Speaker speaker = new Speaker(90);

        speaker.showVolume();

        speaker.volumeUp();
        speaker.showVolume();

        speaker.volumeUp();
        speaker.showVolume();
    }
}</code></pre>
<p>&lt;결과&gt;
<img src="https://velog.velcdn.com/images/baby_potato/post/74590aa9-5a22-455d-96c0-cc7c3bb4b406/image.png" alt=""></p>
<blockquote>
<p>소리를 더 올리고 싶은 새로운 개발자는 max 를 200으로 올렸다가 제품이 고장나게 된다.
volume 필드 직접 접근해서 수정 했다</p>
</blockquote>
<p>&lt;SpeakerMain.java&gt;</p>
<pre><code class="language-java">package access;

public class SpeakerMain {
    public static void main(String[] args) {
        Speaker speaker = new Speaker(90);

        speaker.showVolume();

        speaker.volumeUp();
        speaker.showVolume();

        speaker.volumeUp();
        speaker.showVolume();

        //필드에 직접 접근
        System.out.println(&quot;volume 필드 직접 접근&quot;);
        speaker.volume = 200;
        speaker.showVolume();
    }
}</code></pre>
<p>&lt;결과&gt;
<img src="https://velog.velcdn.com/images/baby_potato/post/2b8dd2d1-9476-4e90-a1ca-ad4d54389fef/image.png" alt=""></p>
<p><strong>why?</strong>
Speaker 객체를 사용하는 사용자는 Speaker 의 volume 필드와 메서드에 모두 접근할 수 있다.
-&gt; 앞서 volumeUp() 과 같은 메서드를 만들어서 음량이 100을 넘지 못하도록 기능을 개발했지만 소용이 없다. 왜냐하면 Speaker 를 사용하는 입장에서는 volume 필드에 직접 접근해서 원하는 값을 설정할 수 있기 때문이다.
=&gt; volume 필드의 외부 접근을 막아야한다.</p>
<p>&lt;Speaker.java&gt;</p>
<pre><code class="language-java">package access;

public class Speaker {
    private int volume; //private 사용

    Speaker(int volume){
        this.volume = volume;
    }
    void volumeUp(){
        if(volume&gt;=100){
            System.out.println(&quot;음량을 증가할 수 없습니다. 최대 음량입니다.&quot;);
        }else {
            volume+=10;
            System.out.println(&quot;음량을 10 증가합니다.&quot;);
        }
    }
    void volumeDown(){
        volume-=10;
        System.out.println(&quot;음량을 10 감소합니다.&quot;);
    }
    void showVolume(){
        System.out.println(&quot;현재 음량: &quot;+ volume);
    }
}</code></pre>
<p>=&gt; private 접근 제어자는 모든 외부 호출을 막는다. 따라서 private 이 붙은 경우 해당 클래스 내부에서만 호출할 수 있다. </p>
<blockquote>
<p>좋은 프로그램은 무한한 자유도가 주어지는 프로그램이 아니라 적절한 제약을 제공하는 프로그램이다.</p>
</blockquote>
<h2 id="접근제어자-종류">접근제어자 종류</h2>
<ul>
<li><p>private : 모든 외부 호출을 막는다.</p>
</li>
<li><p>default (package-private): 같은 패키지안에서 호출은 허용한다.</p>
</li>
<li><p>protected : 같은 패키지안에서 호출은 허용한다. 패키지가 달라도 상속 관계의 호출은 허용한다.</p>
</li>
<li><p>public : 모든 외부 호출을 허용한다</p>
</li>
<li><p>순서대로 private 이 가장 많이 차단하고, public 이 가장 많이 허용한다.
  private -&gt; default -&gt; protected -&gt; public</p>
</li>
</ul>
<h3 id="package-private">package-private</h3>
<ul>
<li>접근 제어자를 명시하지 않으면 같은 패키지 안에서 호출을 허용하는 default 접근 제어자가 적용</li>
<li>동일한 패키지 내의 다른 클래스에서만 접근이 가능</li>
</ul>
<h3 id="접근제어자-사용위치">접근제어자 사용위치</h3>
<p>접근 제어자는 필드와 메서드, 생성자에 사용된다. 추가로 클래스 레벨에도 일부 접근 제어자를 사용할 수 있다..</p>
<h3 id="예시">예시</h3>
<pre><code class="language-java">public class Speaker { //클래스 레벨
 private int volume; //필드
 public Speaker(int volume) {} //생성자
 public void volumeUp() {} //메서드
 public void volumeDown() {}
 public void showVolume() {}
}</code></pre>
<h3 id="접근-제어자의-핵심은-속성과-기능을-외부로부터-숨기는-것">접근 제어자의 핵심은 속성과 기능을 외부로부터 숨기는 것</h3>
<ul>
<li>private 은 나의 클래스 안으로 속성과 기능을 숨길 때 사용, 외부 클래스에서 해당 기능을 호출할 수 없다.</li>
<li>default 는 나의 패키지 안으로 속성과 기능을 숨길 때 사용, 외부 패키지에서 해당 기능을 호출할 수 없다.</li>
<li>protected 는 상속 관계로 속성과 기능을 숨길 때 사용, 상속 관계가 아닌 곳에서 해당 기능을 호출할 수 없다.</li>
<li>public 은 기능을 숨기지 않고 어디서든 호출할 수 있게 공개한다.</li>
</ul>
<h2 id="접근제어자-사용---필드메서드">접근제어자 사용 - 필드,메서드</h2>
<p>&lt;access.a.AccessData.java&gt;</p>
<pre><code class="language-java">package access.a;

public class AccessData {
    public int publicField;
    int defaultField;
    private int privateField;

    public void publicMethod(){
        System.out.println(&quot;publicMethod 호출 &quot;+ publicField);
    }
    void defaultMethod(){
        System.out.println(&quot;defaultMethod 호출 &quot;+ defaultField);
    }
    void privateMethod(){
        System.out.println(&quot;privateMethod 호출 &quot;+ privateField);
    }

    public void innerAccess(){
        System.out.println(&quot;내부 호출&quot;);
        publicField = 100;
        defaultField = 200;
        privateField = 300;
        publicMethod();
        defaultMethod();
        privateMethod();
    }
}</code></pre>
<p>=&gt; innerAccess()는 내부호출 메서드. 내부호출은 자기자신에게 접근하라는 것 = private을 포함한 모든 곳에 접근 가능 </p>
<p>&lt;access.a.AccessInnerMain.java&gt;</p>
<pre><code class="language-java">package access.a;

public class AccessInnerMain {
    public static void main(String[] args) {
        AccessData data = new AccessData();

        //public 호출 가능
        data.publicField =1;
        data.publicMethod();

        //같은 패키지 default 호출가능
        data.defaultField = 2;
        data.defaultMethod();

        //private 호출 불가
//        data.privateField =3;
//        data.privateMethod();

        data.innerAccess();
    }
}</code></pre>
<p>=&gt; public : 모든 접근 허용
    default : <strong>같은 패키지만 허용</strong>
&lt;결과&gt;
<img src="https://velog.velcdn.com/images/baby_potato/post/8423a3fa-5096-4ee0-9bd4-fa6aff5a2f21/image.png" alt=""></p>
<p>&lt;access.a.AccessOuterMain.java&gt;</p>
<pre><code class="language-java">package access.b;

import access.a.AccessData;

public class AccessOuterMain {
    public static void main(String[] args) {
        AccessData data = new AccessData();

        //public 호출 가능
        data.publicField =1;
        data.publicMethod();

        //같은 패키지 default 호출가능
//        data.defaultField = 2;
//        data.defaultMethod();

        //private 호출 불가
//        data.privateField =3;
//        data.privateMethod();

        data.innerAccess();
    }
}</code></pre>
<p>&lt;결과&gt;
<img src="https://velog.velcdn.com/images/baby_potato/post/e702aa5d-ec5b-4863-91eb-e174e82997da/image.png" alt=""></p>
<h2 id="접근제어자-사용---클래스레벨">접근제어자 사용 - 클래스레벨</h2>
<h3 id="클래스-레벨의-접근-제어자-규칙">클래스 레벨의 접근 제어자 규칙</h3>
<ul>
<li>클래스 레벨의 접근 제어자는 public , default 만 사용할 수 있다.
  private , protected 는 사용할 수 없다.</li>
<li>public 클래스는 반드시 파일명과 이름이 같아야 한다.
  하나의 자바 파일에 public 클래스는 하나만 등장할 수 있다.
  하나의 자바 파일에 default 접근 제어자를 사용하는 클래스는 무한정 만들 수 있다.</li>
</ul>
<p>&lt;access.a.PublicClass.java&gt;</p>
<pre><code class="language-java">package access.a;

public class PublicClass {
    public static void main(String[] args) {
        PublicClass publicClass = new PublicClass();
        DefaultClass1 class1 = new DefaultClass1();
        DefaultClass2 class2 = new DefaultClass2();

    }
}
class DefaultClass1{

}
class DefaultClass2{

}</code></pre>
<p>&lt;access.a.PublicClassInnerMain.java&gt;</p>
<pre><code class="language-java">package access.a;

public class PublicClassInnerMain {
    public static void main(String[] args) {
        PublicClass publicClass = new PublicClass();
        DefaultClass1 class1 = new DefaultClass1();
        DefaultClass2 class2 = new DefaultClass2();
    }
}</code></pre>
<p>&lt;access.b.PublicClassOuterMain.java&gt;</p>
<pre><code class="language-java">package access.b;

//import access.a.DefaultClass1;
//import access.a.DefaultClass2;
import access.a.PublicClass;

public class PublicClassOuterMain {
    public static void main(String[] args) {
        PublicClass publicClass = new PublicClass();

        //다른 패키지로 접근 불가
//        DefaultClass1 class1 = new DefaultClass1();
//        DefaultClass2 class2 = new DefaultClass2();
    }
}</code></pre>
<h2 id="캡슐화">캡슐화</h2>
<p>캡슐화(Encapsulation)는 데이터와 해당 데이터를 처리하는 메서드를 하나로 묶어서 외부에서의 접근을 제한하는 것을 말한다. 캡슐화를 통해 데이터의 직접적인 변경을 방지하거나 제한할 수 있다. 캡술화를 안전하게 완성할 수 있게 해주는 장치가 <strong>접근제어자</strong> 이다.</p>
<blockquote>
<p>쉽게 말해서 속성과 기능을 하나로 묶고, 외부에 필요한 기능만 노출하고 나머지는 모두 내부로 숨기는 것</p>
</blockquote>
<h3 id="1-데이터속성-숨기기">1. 데이터(속성) 숨기기</h3>
<p>왠만하면 멤버변수들은 private 으로 막아둔다.</p>
<h3 id="2-기능-숨기기">2. 기능 숨기기</h3>
<p>기능 중 외부에서 사용하지 않고 내부에서만 사용하는 기능들은 숨기는 것이 좋다.</p>
<h3 id="캡슐화-예시">캡슐화 예시</h3>
<p>&lt;BankAccount.java&gt;</p>
<pre><code class="language-java">package access;

public class BankAccount {
    private int balance;

    public BankAccount(){
        balance = 0;
    }

    //public 메서드 : deposit
    public void deposit(int amount){
        if (isAmountValid((amount))) {
            balance += amount;
        }else {
            System.out.println(&quot;유효하지 않은 금액입니다.&quot;);
        }
    }

    public void withdraw(int amount){
        if(isAmountValid(amount) &amp;&amp; balance -amount &gt;=0){
            balance -= amount;
        }else{
            System.out.println(&quot;유효하지 않은 금액이거나 잔액이 부족합니다.&quot;);
        }
    }

    public int getBalance(){
        return balance;
    }

    private boolean isAmountValid(int amount){
        return amount&gt;0;
    }
}</code></pre>
<p>&lt;BankAccountMain.java&gt;</p>
<pre><code class="language-java">package access;

public class BankAccountMain {
    public static void main(String[] args) {
        BankAccount account = new BankAccount();
        account.deposit(10000);
        account.withdraw(3000);
        System.out.println(&quot;balance = &quot;+account.getBalance());
    }
}</code></pre>
<p>isAmountValid 가 public 이라면 개발자는 해당 메서드를 사용해서 검증해야하는지 의문을 가질 수가 있다.</p>
<h2 id="문제와-풀이1">문제와 풀이1</h2>
<p>&lt;MaxCounter.java&gt;</p>
<pre><code class="language-java">package access.ex;

public class MaxCounter {
    private int count;
    public int max;

    public MaxCounter(int max){
        this.max = max;
    }

    public void increment(){
        //검증로직
        if(count&gt;=max){
            System.out.println(&quot;최대값을 초과할 수 없습니다.&quot;);
            return; //검즈에서 통과 못하면 실행으로 넘어가지 못함, else 를 사용하지 않아도됨
        }
        //실행 로직
        count++;
    }

    public int getCount(){
        return count;
    }
}</code></pre>
<p>&lt;CounterMain.java&gt;</p>
<pre><code class="language-java">package access.ex;

public class CounterMain {
    public static void main(String[] args) {
        MaxCounter maxCounter = new MaxCounter(3);

        maxCounter.increment();
        maxCounter.increment();
        maxCounter.increment();
        maxCounter.increment();
        int count = maxCounter.getCount();
        System.out.println(count);
    }
}</code></pre>
<h2 id="문제와-풀이2">문제와 풀이2</h2>
<p>&lt;Item.java&gt;</p>
<pre><code class="language-java">package access.ex;

public class Item {
    private String name;
    private int price;

    private int quantity;

    public  Item(String name, int price, int quantity){
        this.name = name;
        this.price = price;
        this. quantity = quantity;
    }

    public String getName(){
        return name;
    }

    public int getTotalProce(){
        return price*quantity;
    }
}</code></pre>
<p>&lt;ShoppingCart.java&gt;</p>
<pre><code class="language-java">package access.ex;

public class ShoppingCart {
    private  Item[] items = new Item[10];
    private int itemCount;

    public void addItem(Item item){
        if(itemCount &gt;= items.length){
            System.out.println(&quot;장바구니가 꽉 찼습니다.&quot;);
            return;
        }

        items[itemCount] = item;
        itemCount++;
    }

    public  void displayItems(){
        System.out.println(&quot;장바구니 상품 출력&quot;);
        for (int i=0; i&lt;itemCount; i++){
            Item item = items[i];
            System.out.println(&quot;상품명: &quot;+ item.getName()+&quot;  합계: &quot;+ item.getTotalProce());
        }

        System.out.println(&quot;전체 가격 합 : &quot; + calTotalPrice());
    }

    private int calTotalPrice(){
        int totalPrice = 0;
        for (int i=0; i&lt;itemCount; i++){
            Item item = items[i];
            totalPrice += item.getTotalProce();
        }
        return totalPrice;
    }


}</code></pre>
<p>&lt;ShoppingCartMain.java&gt;</p>
<pre><code class="language-java">package access.ex;

public class ShoppingCartMain {
    public static void main(String[] args) {
        ShoppingCart shoppingCart = new ShoppingCart();

        Item item1 = new Item(&quot;사과&quot;,1000,2);
        Item item2 = new Item(&quot;바나나&quot;,500,3);
        Item item3 = new Item(&quot;망고&quot;,3000,2);

        shoppingCart.addItem(item1);
        shoppingCart.addItem(item2);
        shoppingCart.addItem(item3);

        shoppingCart.displayItems();
    }
}</code></pre>
<p>&lt;결과&gt;
<img src="https://velog.velcdn.com/images/baby_potato/post/221fd5bc-effe-4eb7-855a-e2976f0adb1d/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[문자열 데이터 엑셀 파일로 적재과정 중 생긴 문제들..]]></title>
            <link>https://velog.io/@baby_potato/%EB%AC%B8%EC%9E%90%EC%97%B4-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%97%91%EC%85%80-%ED%8C%8C%EC%9D%BC%EB%A1%9C-%EC%A0%81%EC%9E%AC%EA%B3%BC%EC%A0%95-%EC%A4%91-%EC%83%9D%EA%B8%B4-%EB%AC%B8%EC%A0%9C</link>
            <guid>https://velog.io/@baby_potato/%EB%AC%B8%EC%9E%90%EC%97%B4-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%97%91%EC%85%80-%ED%8C%8C%EC%9D%BC%EB%A1%9C-%EC%A0%81%EC%9E%AC%EA%B3%BC%EC%A0%95-%EC%A4%91-%EC%83%9D%EA%B8%B4-%EB%AC%B8%EC%A0%9C</guid>
            <pubDate>Fri, 10 May 2024 10:25:33 GMT</pubDate>
            <description><![CDATA[<h1 id="history1">History1</h1>
<p>data는 단순히 예시이며 실제 데이터는 솔루션 결과들이었다!</p>
<pre><code class="language-js">const data = &#39;John,Doe|1\n Jane|2\nMary,a|3&#39;</code></pre>
<p>이런 데이터를 
<img src="https://velog.velcdn.com/images/baby_potato/post/1e7197a9-eba7-49ab-8b58-bd4a3ab5d333/image.png" alt="">
A열에만 &#39;\n&#39; 기준으로 행으로 쌓인 엑셀파일로 변환하고 싶었다.
엑셀 파일은 /data/test.xlsx 였다.</p>
<h1 id="problem-1">Problem 1</h1>
<p>그런데 <img src="https://velog.velcdn.com/images/baby_potato/post/d30c4e32-7b92-4605-bac6-867e7c59a1ab/image.png" alt="">
이렇게 쌓이는 데이터를 발견했고, 
쉼표 기준으로 열이 분리된다는 엑셀의 특징을 알게되었다.</p>
<h1 id="solve-2">Solve 2</h1>
<p>기존의 나의 코드는 단순했다.</p>
<pre><code class="language-js">const dir = &#39;/data/test.csv&#39;;
const data = &#39;John,Doe|1\n Jane|2\nMary,a|3&#39;;
fsPromises.appendFile(dir,data); //nodeJs 라이브러리</code></pre>
<p>쉼표를 문자취급을 해줘야하기때문에 &#39;&#39;으로 감싸야 했고 그래서 나는 data 의 구조를 배열로 변경했다. 그리고 xlsx 라이브러리를 사용하기로 했다.</p>
<pre><code class="language-js">const dir = &#39;/data/test.csv&#39;;
//구조변경
const data = [&#39;John,Doe|1&#39;, &#39;Jane|2&#39;, &#39;Mary,a|3&#39;];

//엑셀 워크북 생성
const workbook = xlsx.utils.book_new();
//각 데이터가 시트의 한 행 (2차원배열)
const worksheet = xlsx.utils.aoa_to_sheet(data.map(row =&gt; [row]));

//시트를 워크북에 추가
xlsx.utils.book_append_sheet(workbook, worksheet, &#39;Sheet1&#39;);

// 엑셀 파일 저장 (디렉토리를 포함한 경로)
xlsx.writeFile(workbook, dir);
</code></pre>
<p>이렇게 하니 원하는대로 적재가 되었다!</p>
<h1 id="history2">History2</h1>
<p>다 된줄 알았으나 여러가지의 엑셀파일을 하나의 엑셀로 합치려고 하다가 또다른 이슈를 발견했다.
&lt;/a/name.csv&gt;</p>
<pre><code class="language-xlsx">John,Doe|1 
Jane|2</code></pre>
<p>&lt;/b/name.csv&gt;</p>
<pre><code class="language-xlsx">Dan|1 
Sarah|8</code></pre>
<p>이런 2개의 엑셀을 
&lt;/total/name.csv&gt;
<img src="https://velog.velcdn.com/images/baby_potato/post/c05d97a0-1e94-4cb3-81e3-16192978375c/image.png" alt="">
이렇게 하나의 엑셀로 합쳐서 전달하려했다.</p>
<h1 id="problem2">Problem2</h1>
<p>문제는.. total/name.csv 에 a/name.csv를 적재하고 덮어쓰기로 b/name.csv 를 적재하는데 결과가
<img src="https://velog.velcdn.com/images/baby_potato/post/a2745658-4778-41cb-8c59-79de5f5a91d7/image.png" alt="">
이런식으로 a.csv의 마지막행과 b.csv의 첫번째 행이 합쳐진다는 문제였다.</p>
<p>우선 각각 a,b 디렉토리에서 name.csv 파일을 읽어오고, 
그 파일들을 name.csv 로 합치는 로직이었고 쉘스크립트로 작성하였다.</p>
<pre><code class="language-shell">for ((i=1; i${#dirList[@]}; i++));
do
    # 각각의 디렉토리 접근 (a,b 디렉토리)
    VALUE=${dirList[i]};
    cd ${VALUE##*/}

    csvList=( `find . -type f` );
    #디렉토리 csv 파일 탐색
    for((j=0; j&lt;${#csvList[@]}; j++));
    do
        #덮어쓰기
        cat ${csvList[j]} &gt;&gt; /total/name.csv
    done 
done</code></pre>
<h1 id="solve2">Solve2</h1>
<p>덮어쓰는 부분에서 cat 대신 tail -n +1 로 사용해 보았는데 b.csv 의 첫행이 날아가고 두번째 행부터 적재가 되더라..</p>
<p>생각보다 해결방안은 단순했다. 
줄바꿈 이었다!
처음엔 csv 내에 마지막행에 직접 &#39;\n&#39;으로 해볼까도 싶었는데 그러면 데이터를 직접 건드리는 거라 쉘에서 처리 해보기로 했다.</p>
<pre><code class="language-shell">for ((i=1; i${#dirList[@]}; i++));
do
    # 각각의 디렉토리 접근 (a,b 디렉토리)
    VALUE=${dirList[i]};
    cd ${VALUE##*/}

    csvList=( `find . -type f` );
    #디렉토리 csv 파일 탐색
    for((j=0; j&lt;${#csvList[@]}; j++));
    do
        #덮어쓰기
        cat ${csvList[j]} &gt;&gt; /total/name.csv
        #줄바꿈처리
        echo &quot;&quot; &gt;&gt; /total/name.csv
    done 
done</code></pre>
<p>이렇게 하면 각 파일의 내용에 별도의 행이 추가된다.</p>
<h1 id="end">End</h1>
<p>빅데이터팀과 협업하게 되면 DB 에 직접 데이터를 쌓아주는게 좋지만 지금 프로젝트에서는 빅데이터팀 DB에 접근할 수 있는 환경이 안돼서 엑셀 파일로 전달을 하게 되었다. (빅데이터의 원천데이터들은 txt나 엑셀 파일로 이루어지면 데이터 관리와 보정이 편하기 때문일 듯)</p>
<p>엑셀파일로 변환하는 작업은 이렇게 쉼표기준으로 분리되거나 한글변환이 안되거나 등 이슈가 많음으로 꼭꼭 많은 검토를 통해 이슈사항을 확인해야하는 것 같다.</p>
<p>그리고 쉘은 crontab 을 사용하여 호출하게 했다~!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[2.3~4.Java - 객체 지향 프로그래밍, 생성자]]></title>
            <link>https://velog.io/@baby_potato/2.34.Java-%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%83%9D%EC%84%B1%EC%9E%90</link>
            <guid>https://velog.io/@baby_potato/2.34.Java-%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%83%9D%EC%84%B1%EC%9E%90</guid>
            <pubDate>Sat, 04 May 2024 09:57:06 GMT</pubDate>
            <description><![CDATA[<h1 id="객체지향프로그래밍">객체지향프로그래밍</h1>
<h2 id="절차지향프로그래밍">절차지향프로그래밍</h2>
<ul>
<li><p>절차지향 프로그래밍
  실행 순서를 중요시 하는 프로그래밍, 프로그램의 흐름을 순차적으로 따르며 처리하는 방식</p>
</li>
<li><p>객체 지향 프로그래밍
  객체를 지향한다. 쉽게 이야기해서 객체를 중요하게 생각하는 방식
  객체 지향 프로그래밍은 실제 세계의 사물이나 사건을 객체로 보고, 이러한 객체들 간의 상호작용을 중심으로 프로그래밍하는 방식</p>
</li>
<li><p>차이점 : 절차지향은 데이터와 해당 데이터에 대한 처리 방식이 분리되어 있다. 객체지향은 데이터와 그 데이터에 대한 행동(메서드)이 하나의 &#39;객체&#39; 안에 포함되어 있다.</p>
</li>
</ul>
<h3 id="음악-플레이어-만들기">음악 플레이어 만들기</h3>
<p>절차 지향 프로그래밍을 만들고 객체 지향으로 바꿔보자!</p>
<p>&lt;MusicPalyerMain1.java&gt;</p>
<pre><code class="language-java">package oop1;

public class MusicPalyerMain1 {
    public static void main(String[] args) {
        int volume = 0;
        boolean isOn = false;

        //음악 플레이어 켜기
        isOn = true;
        System.out.println(&quot;음악 플레이어를 시작합니다&quot;);

        //볼륨 증가
        volume++;
        System.out.println(&quot;음악 플레이어 볼륨:&quot; + volume);

        //볼륨 증가
        volume++;
        System.out.println(&quot;음악 플레이어 볼륨:&quot; + volume);

        //볼륨 감소
        volume--;
        System.out.println(&quot;음악 플레이어 볼륨:&quot; + volume);

        //음악 플레이어 상태
        System.out.println(&quot;음악 플레이어 상태 확인&quot;);
        if (isOn) {
            System.out.println(&quot;음악 플레이어 ON, 볼륨:&quot; + volume);
        } else {
            System.out.println(&quot;음악 플레이어 OFF&quot;);
        }

        //음악 플레이어 끄기
        isOn = false;
        System.out.println(&quot;음악 플레이어를 종료합니다&quot;);

    }
}</code></pre>
<p>&lt;결과&gt;
<img src="https://velog.velcdn.com/images/baby_potato/post/37a15e9a-f36a-4d0c-aa4e-8f0beccf6ffb/image.png" alt=""></p>
<h4 id="데이터-묶음">데이터 묶음</h4>
<p>MusicPlayerData 라는 클래스로 사용되는 데이터들을 여기에 묶어서 멤버변수로 사용하자
&lt;MusicPlayerData.java&gt;</p>
<pre><code class="language-java">package oop1;

public class MusicPlayerData {
    int volume=0;
    boolean isOn = false;
}</code></pre>
<p>&lt;MusicPalyerMain2.java&gt;</p>
<pre><code class="language-java">package oop1;

public class MusicPalyerMain2 {
    public static void main(String[] args) {
        MusicPlayerData data = new MusicPlayerData();

        //음악 플레이어 켜기
        data.isOn = true;
        System.out.println(&quot;음악 플레이어를 시작합니다&quot;);

        //볼륨 증가
        data.volume++;
        System.out.println(&quot;음악 플레이어 볼륨:&quot; + data.volume);

        //볼륨 증가
        data.volume++;
        System.out.println(&quot;음악 플레이어 볼륨:&quot; + data.volume);

        //볼륨 감소
        data.volume--;
        System.out.println(&quot;음악 플레이어 볼륨:&quot; + data.volume);

        //음악 플레이어 상태
        System.out.println(&quot;음악 플레이어 상태 확인&quot;);
        if (data.isOn) {
            System.out.println(&quot;음악 플레이어 ON, 볼륨:&quot; + data.volume);
        } else {
            System.out.println(&quot;음악 플레이어 OFF&quot;);
        }

        //음악 플레이어 끄기
        data.isOn = false;
        System.out.println(&quot;음악 플레이어를 종료합니다&quot;);

    }
}</code></pre>
<p>다양한 변수들이 추가되더라도 쉽게 관리 할 수 있다.</p>
<h4 id="메서드-추출">메서드 추출</h4>
<p>중복되는 부분들을 정리하자 -&gt; 재사용하기 쉬움
&lt;MusicPlayerMain3.java&gt;</p>
<pre><code class="language-java">package oop1;

public class MusicPalyerMain3 {
    public static void main(String[] args) {
        MusicPlayerData data = new MusicPlayerData();

        //음악 플레이어 켜기
        on(data);

        //볼륨 증가
       volumeUp(data);

        //볼륨 증가
        volumeUp(data);

        //볼륨 감소
        volumeDown(data);

        //음악 플레이어 상태
        showStatus(data);

        //음악 플레이어 끄기
        off(data);
    }

    static void on(MusicPlayerData data){
        data.isOn = true;
        System.out.println(&quot;음악 플레이어를 시작합니다&quot;);
    }
    static void off(MusicPlayerData data){
        data.isOn = false;
        System.out.println(&quot;음악 플레이어를 종료합니다&quot;);
    }

    static void volumeUp(MusicPlayerData data){
        data.volume++;
        System.out.println(&quot;음악 플레이어 볼륨:&quot; + data.volume);
    }
    static void volumeDown(MusicPlayerData data){
        data.volume--;
        System.out.println(&quot;음악 플레이어 볼륨:&quot; + data.volume);
    }

    static void showStatus(MusicPlayerData data){
        System.out.println(&quot;음악 플레이어 상태 확인&quot;);
        if (data.isOn) {
            System.out.println(&quot;음악 플레이어 ON, 볼륨:&quot; + data.volume);
        } else {
            System.out.println(&quot;음악 플레이어 OFF&quot;);
        }
    }
}</code></pre>
<p>각각의 기능을 메서드로 만들어 모듈화 시켰다.
=&gt; 중복 제거, 변경영향범위(기능 수정 시 해당 메서드만 수정), 메서드이름을 통해 코드를 쉽게 이해할 수 있다.</p>
<p><strong>절차지향프로그램의 한계</strong>
데이터(MusicPlayerData)와 기능(MusicPlayerMain3)이 분리되어 있어서 유지보수 관점에서 2곳을 관리하여야한다.
=&gt; 객체지향 프로그램으로 데이터와 기능을 온전히 하나로 묶어서 사용</p>
<h2 id="클래스와-메서드">클래스와 메서드</h2>
<p>&lt;ValueData.java&gt;</p>
<pre><code class="language-java">package oop1;

public class ValueData {
    int value;
}</code></pre>
<p>&lt;ValueDataMain.java&gt;</p>
<pre><code class="language-java">package oop1;

public class ValueDataMain {
    public static void main(String[] args) {
        ValueData valueData = new ValueData();

        add(valueData);
        add(valueData);
        add(valueData);

        System.out.println(&quot;최종 숫자 = &quot;+ valueData.value);
    }
    static void add(ValueData valueData){
        valueData.value++;
        System.out.println(&quot;숫자증가 value= &quot; + valueData.value);
    }
}</code></pre>
<p>&lt;결과&gt;
<img src="https://velog.velcdn.com/images/baby_potato/post/9d25afa4-edd0-4d13-99db-f9730042b775/image.png" alt=""></p>
<h3 id="객체지향">객체지향</h3>
<p>클래스 내부에 속성(데이터)과 기능(메서드)을 함께 포함할 수 있다.
&lt;ValueData.java&gt;</p>
<pre><code class="language-java">package oop1;

public class ValueData {
    int value;

    void add(){
        value++;
        System.out.println(&quot;숫자증가 value= &quot; + value);
    }
}</code></pre>
<p>&lt;ValueObjectMain.java&gt;</p>
<pre><code class="language-java">package oop1;

public class ValueObjectMain {
    public static void main(String[] args) {
        ValueData valueData = new ValueData();

        valueData.add();
        valueData.add();
        valueData.add();

        System.out.println(&quot;최종 숫자 = &quot; + valueData.value);
    }
}</code></pre>
<blockquote>
<ul>
<li>클래스는 속성(데이터, 멤버 변수)과 기능(메서드)을 정의할 수 있다.</li>
</ul>
</blockquote>
<ul>
<li>객체는 자신의 메서드를 통해 자신의 멤버 변수에 접근할 수 있다</li>
<li>객체의 메서드 내부에서 접근하는 멤버 변수는 객체 자신의 멤버 변수이다.</li>
</ul>
<h2 id="객체지향-프로그래밍">객체지향 프로그래밍</h2>
<p>음악 플레이어를 만들어서 제공하는 개발자와 플레이어를 사용하는 개발자가 분리되어있다고 생각하자!</p>
<blockquote>
<ul>
<li>속성 : volume, isOn</li>
</ul>
</blockquote>
<ul>
<li>기능 : on(), off(), volumeUp(), volumeDown(), showStatus()</li>
</ul>
<p>&lt;MusicPlayer.java&gt;</p>
<pre><code class="language-java">package oop1;

public class MusicPlayer {
    int volume =0;
    boolean isOn =false;

    void on(){
        isOn = true;
        System.out.println(&quot;음악 플레이어를 시작합니다&quot;);
    }
    void off(){
        isOn = false;
        System.out.println(&quot;음악 플레이어를 종료합니다&quot;);
    }

    void volumeUp(){
        volume++;
        System.out.println(&quot;음악 플레이어 볼륨:&quot; + volume);
    }
    void volumeDown(){
        volume--;
        System.out.println(&quot;음악 플레이어 볼륨:&quot; + volume);
    }

    void showStatus(){
        System.out.println(&quot;음악 플레이어 상태 확인&quot;);
        if (isOn) {
            System.out.println(&quot;음악 플레이어 ON, 볼륨:&quot; + volume);
        } else {
            System.out.println(&quot;음악 플레이어 OFF&quot;);
        }
    }
}</code></pre>
<p>=&gt; 음악 플레이어를 사용하는데 필요한 모든 속성과 기능이 하나의 클래스에 포함되어 있다</p>
<p>&lt;MusicPlayerMain4.java&gt;</p>
<pre><code class="language-java">package oop1;

public class MusicPlayerMain4 {
    public static void main(String[] args) {
        MusicPlayer player = new MusicPlayer();

        //음악 플레이어 켜기
        player.on();
        //볼륨 증가
        player.volumeUp();
        //볼륨 증가
        player.volumeUp();
        //볼륨 감소
        player.volumeDown();
        //음악 플레이어 상태
        player.showStatus();
        //음악 플레이어 끄기
        player.off();

    }
}</code></pre>
<p>MusicPlayer 를 사용하는 입장에서는 이제 MusicPlayer 내부에 어떤 속성(데이터)이 있는지 전혀 몰라도 된다. MusicPlayer 를 사용하는 입장에서는 단순하게 MusicPlayer 가 제공하는 기능 중에 필요한 기능을 호출해서 사용하기만 하면 된다.</p>
<h4 id="캡슐화">캡슐화</h4>
<p>MusicPlayer 를 보면 음악 플레이어를 구성하기 위한 속성과 기능이 마치 하나의 캡슐에 쌓여있는 것 같다. 이렇게 속성과 기능을 하나로 묶어서 필요한 기능을 메서드를 통해 외부에 제공하는 것을 <strong>캡슐화</strong>라 한다.</p>
<h2 id="문제와-풀이">문제와 풀이</h2>
<p>&lt;RectangleProceduralMain.java&gt;</p>
<pre><code class="language-java">package oop1.ex;

public class RectangleProceduralMain {
    public static void main(String[] args) {
        int width=5;
        int height=8;

        int area = calculateArea(width, height);
        System.out.println(&quot;넓이: &quot; + area);
        int perimeter = calculatePerimeter(width, height);
        System.out.println(&quot;둘레 길이: &quot; + perimeter);
        boolean square = isSquare(width, height);
        System.out.println(&quot;정사각형 여부: &quot; + square);

    }
    static int calculateArea(int width,int height){
        int area = width*height;
        return area;
    }
    static int calculatePerimeter(int width,int height){
        int perimeter = (width+height)*2;
        return perimeter;
    }
    static boolean isSquare(int width,int height){
        return width == height;
    }
}</code></pre>
<h4 id="객체-지향">객체 지향</h4>
<p>&lt;RectangleProceduralMain.java&gt;</p>
<pre><code class="language-java">package oop1.ex;

public class RectangleProceduralMain {
    public static void main(String[] args) {
        Rectangle rectangle = new Rectangle();
        rectangle.width=5;
        rectangle.height=8;

        int area = rectangle.calculateArea();
        System.out.println(&quot;넓이: &quot; + area);
        int perimeter = rectangle.calculatePerimeter();
        System.out.println(&quot;둘레 길이: &quot; + perimeter);
        boolean square = rectangle.isSquare();
        System.out.println(&quot;정사각형 여부: &quot; + square);

    }

}</code></pre>
<p>&lt;Rectangle.java&gt;</p>
<pre><code class="language-java">package oop1.ex;

public class RectangleProceduralMain {
    public static void main(String[] args) {
        Rectangle rectangle = new Rectangle();
        rectangle.width=5;
        rectangle.height=8;

        int area = rectangle.calculateArea();
        System.out.println(&quot;넓이: &quot; + area);
        int perimeter = rectangle.calculatePerimeter();
        System.out.println(&quot;둘레 길이: &quot; + perimeter);
        boolean square = rectangle.isSquare();
        System.out.println(&quot;정사각형 여부: &quot; + square);

    }

}</code></pre>
<h2 id="문제와-풀이2">문제와 풀이2</h2>
<p>&lt;Account.java&gt;</p>
<pre><code class="language-java">package oop1.ex;

public class Account {
    int balance;

    void deposit(int amount){
        balance+=amount;
    }
    void withdraw(int amount){
        if(balance&gt;=amount){
            balance-=amount;
        }else{
            System.out.println(&quot;잔액이 부족합니다.&quot;);
        }
    }
}</code></pre>
<p>&lt;AccountMain.java&gt;</p>
<pre><code class="language-java">package oop1.ex;

public class AccountMain {
    public static void main(String[] args) {
        Account account = new Account();

        account.deposit(10000);
        account.withdraw(9000);
        account.withdraw(2000);

        System.out.println(&quot;잔고 : &quot;+ account.balance);

    }
}</code></pre>
<h1 id="생성자">생성자</h1>
<h2 id="필요한-이유">필요한 이유</h2>
<p>객체를 생성하는 시점에 어떤 작업을 하고 싶다면 생성자(Constructor)를 이용하면 된다.
&lt;MemberInit.java&gt;</p>
<pre><code class="language-java">package construct;

public class MemberInit {
    String name;
    int age;
    int grade;

}</code></pre>
<p>&lt;MemberInitMain1.java&gt;</p>
<pre><code class="language-java">package construct;

public class MethodInitMain1 {
    public static void main(String[] args) {
        MemberInit member1= new MemberInit();

        member1.name = &quot;user1&quot;;
        member1.age = 15;
        member1.grade=90;

        MemberInit member2 = new MemberInit();
        member2.name = &quot;user2&quot;;
        member2.age = 16;
        member2.grade=80;

        MemberInit[] members = {member1,member2};

        for(MemberInit s: members){
            System.out.println(&quot;이름: &quot;+s.name+&quot;  나이:&quot;+s.age+&quot;  성적:&quot;+s.grade);
        }
    }
}</code></pre>
<p>=&gt; 회원의 초기값을 설정하는 부분이 반복 된다. 메서드(initMember)를 사용하여 반복을 제거하자.
&lt;MemberInitMain2.java&gt;</p>
<pre><code class="language-java">package construct;

public class MethodInitMain2 {
    public static void main(String[] args) {
        MemberInit member1= new MemberInit();
        initMember(member1,&quot;user1&quot;,15,90);

        MemberInit member2 = new MemberInit();
        initMember(member2,&quot;user2&quot;,16,80);

        MemberInit[] members = {member1,member2};

        for(MemberInit s: members){
            System.out.println(&quot;이름:&quot;+ s.name+&quot;  나이:&quot;+s.age+ &quot;  성적: &quot;+ s.grade);
        }
    }
    static void initMember(MemberInit member, String name, int age, int grade){
        member.name = name;
        member.age = age;
        member.grade = grade;
    }
}</code></pre>
<h2 id="this">this</h2>
<p>&lt;MemberInit.java&gt; 에 initMember() 추가</p>
<pre><code class="language-java">package construct;

public class MemberInit {
    String name;
    int age;
    int grade;

    //추가
    void initMember(String name, int age, int grade){
        this.name = name;
        this.age = age;
        this.grade = grade;
    }
}</code></pre>
<p>&lt;MemberInitMain3.java&gt;</p>
<pre><code class="language-java">package construct;

public class MethodInitMain3 {
    public static void main(String[] args) {
        MemberInit member1= new MemberInit();
        member1.initMember(&quot;user1&quot;,15,90);

        MemberInit member2 = new MemberInit();
        member2.initMember(&quot;user2&quot;,16,80);

        MemberInit[] members = {member1,member2};

        for(MemberInit s: members){
            System.out.println(&quot;이름:&quot;+ s.name+&quot;  나이:&quot;+s.age+ &quot;  성적: &quot;+ s.grade);
        }
    }
}</code></pre>
<p><strong>this</strong></p>
<ul>
<li>this.name =name 에서 name 을 구분하는 법?, 멤버변수와 매개변수의 이름이 똑같은 때 구분하는 법?
  =&gt; 멤버변수보다 매개변수가 코드블럭의 더 안쪽에 있기 때문에 매개변수가 우선순위를 가진다.</li>
<li>둘의 이름이 같을 땐 this를 사용해서 명확히 구분 해야한다.</li>
<li>멤버변수에 접근하려면 this를 사용</li>
<li>this 는 인스턴스 자신을 가리킨다.</li>
</ul>
<h3 id="this의-생략">this의 생략</h3>
<p>this 는 생략할 수 있다. 이 경우 변수를 찾을 때 가까운 지역변수(매개변수도 지역변수다)를 먼저 찾고 없으면 그 다음으로 멤버 변수를 찾는다. 멤버 변수도 없으면 오류가 발생한다.</p>
<p>&lt;MemberThis.java&gt;</p>
<pre><code class="language-java">package construct;

public class MemberThis {
    String nameField;

    void initMember(String nameParameter){
        nameField = nameParameter;
    }
}</code></pre>
<h3 id="this의-코딩-스타일">this의 코딩 스타일</h3>
<p>항상 this를 사용하는 스타일 
&lt;MemberThis.java&gt;</p>
<pre><code class="language-java">package construct;

public class MemberThis {
    String nameField;

    void initMember(String nameParameter){
        this.nameField = nameParameter;
    }
}</code></pre>
<p>=&gt; 헷갈릴 땐 그냥 추가 해도된다. 이렇게하면 이 코드가 멤버 변수를 사용한다는 것을 눈으로 쉽게 확인할 수 있다. BUT 요즘은 잘 안쓴다. 권장하지 않는다. </p>
<h2 id="도입">도입</h2>
<p>프로그래밍을 하다보면 객체 생성후 바로 초기값을 할당해야하는 경우가 생기는데 그럴 때마다 매번 메서드를 만들어야 한다.
그래서 대부분의 객체지향언어는 객체를 생성하자마자 즉시 필요한 기능을 좀 더 편리하게 수행할 수 있도록 <strong>생성자</strong>라는 기능을 제공한다.</p>
<p>&lt;MemberConstruct.java&gt;</p>
<pre><code class="language-java">package construct;

public class MemberConstruct {
    String name;
    int age;
    int grade;

//생성자
    MemberConstruct(String name, int age, int grade){
        System.out.println(&quot;생성자 호출 name= &quot;+name+&quot;, age=&quot;+age+&quot;, grade=&quot;+grade);
        this.name=name;
        this.age=age;
        this.grade = grade;
    }
}</code></pre>
<blockquote>
<ul>
<li>생정자의 이름은 클래스 이름과 동일해야한다. 따라서 첫글자도 대문자로 시작한다.</li>
</ul>
</blockquote>
<ul>
<li>생정자는 반환타입이 없다.</li>
<li>나머지는 메서드와 동일</li>
</ul>
<p>&lt;ConstructMain1.java&gt;</p>
<pre><code class="language-java">package construct;

public class ConstructMain1 {
    public static void main(String[] args) {
        MemberConstruct member1 = new MemberConstruct(&quot;user1&quot;, 15,90);
        MemberConstruct member2 = new MemberConstruct(&quot;user2&quot;, 16 ,80);

        MemberConstruct[] members = {member1,member2};

        for (MemberConstruct s : members) {
            System.out.println(&quot;이름:&quot; + s.name + &quot; 나이:&quot; + s.age + &quot; 성적:&quot; +
                    s.grade);
        }
    }
}</code></pre>
<h2 id="생성자-장점">생성자 장점</h2>
<ol>
<li>중복 호출 제거
  객체를 생성하면서 동시에 필요한 작업을 한번에 처리할 수 있다.</li>
<li>객체를 생성할 때 직접 정의한 생성자가 있다면 직접 정의한 생성자를 <strong>반드시</strong> 호출해야 한다.</li>
</ol>
<h2 id="기본생성자">기본생성자</h2>
<p>&lt;MemberInit.java&gt;, &lt;MethodInitMain3.java&gt; 을 보면 알 수 있다.</p>
<ul>
<li>매개변수가 없는 생성자를 기본 생성자라고 한다.</li>
<li>클래스에 생성자가 하나도 없으면 자바 컴파일러는 매개변수가 없고, 작동하는 코드가 없는 기본 생성자를 자동으로 만들어준다.</li>
<li><strong>생성자가 하나라도 있으면 자바는 기본 생성자를 만들지 않는다.</strong></li>
</ul>
<p>&lt;MemberDefault.java&gt;</p>
<pre><code class="language-java">package construct;

public class MemberDefault {
    String name;

    //java에서 기본생성자 만들어줌
//    MemberDefault(){
//
//    }

    //직접 기본생성자 정의
    MemberDefault() {
        System.out.println(&quot;생성자 호출&quot;);
    }
}</code></pre>
<p>&lt;MemberDefaultMain.java&gt;</p>
<pre><code class="language-java">package construct;

public class MemberDefaultMain {
    public static void main(String[] args) {
        MemberDefault memberDefault = new MemberDefault();
    }
}</code></pre>
<h2 id="오버로딩과-this">오버로딩과 this()</h2>
<p>&lt;MemberConstruct.java&gt;</p>
<pre><code class="language-java">package construct;

public class MemberConstruct {
    String name;
    int age;
    int grade;

    //오버로딩 추가
    MemberConstruct(String name, int age){
        this.name =name;
        this.age = age;
        this.grade = 50;
    }

    //변경
//    MemberConstruct(String name, int age){
//        this(name, age,50);
//    }

    //생성자
    MemberConstruct(String name, int age, int grade){
        System.out.println(&quot;생성자 호출 name= &quot;+name+&quot;, age=&quot;+age+&quot;, grade=&quot;+grade);
        this.name=name;
        this.age=age;
        this.grade = grade;
    }
}</code></pre>
<p>기존의 MemberConstruct에 생성자를 하나 추가해서 생성자가 2개가 됨</p>
<pre><code class="language-java">MemberConstruct(String name, int age)
MemberConstruct(String name, int age, int grade) </code></pre>
<p>새로 추가한 생성자는 grade를 받지 않고 50점으로 초기화.</p>
<p>&lt;ConstructMain2.java&gt;</p>
<pre><code class="language-java">package construct;

public class ConstructMain2 {
    public static void main(String[] args) {
        MemberConstruct member1 = new MemberConstruct(&quot;user1&quot;, 15,90);
        MemberConstruct member2 = new MemberConstruct(&quot;user2&quot;, 16);

        MemberConstruct[] members = {member1,member2};

        for (MemberConstruct s : members) {
            System.out.println(&quot;이름:&quot; + s.name + &quot; 나이:&quot; + s.age + &quot; 성적:&quot; +
                    s.grade);
        }
    }
}</code></pre>
<p>&lt;결과&gt;
<img src="https://velog.velcdn.com/images/baby_potato/post/323f303f-02fe-43df-ab32-6c063491de89/image.png" alt="">
=&gt; 오버로딩으로 성적 입력이 필요한 경우에는 grade가 있는 생성자 호출, 없는 경우에는 grade가 없는 생성자 호출!</p>
<p><strong>MemberConstruct.java 의 중복된 코드</strong>
this.name ... =&gt;
&lt;MemberConstruct.java&gt;</p>
<pre><code class="language-java">package construct;

public class MemberConstruct {
    String name;
    int age;
    int grade;

    //오버로딩 추가
//    MemberConstruct(String name, int age){
//        this.name =name;
//        this.age = age;
//        this.grade = 50;
//    }

    //변경
    MemberConstruct(String name, int age){
        this(name, age,50);
    }

    //생성자
    MemberConstruct(String name, int age, int grade){
        System.out.println(&quot;생성자 호출 name= &quot;+name+&quot;, age=&quot;+age+&quot;, grade=&quot;+grade);
        this.name=name;
        this.age=age;
        this.grade = grade;
    }
}</code></pre>
<p>첫번째 생성자 내부에서 두번째 생성자를 호출한다.</p>
<blockquote>
<p>MemberConstruct(String name, int age) -&gt; MemberConstruct(String name, int age,int grade)</p>
</blockquote>
<p>=&gt; this()를 사용하면 생성자 내부에서 다른 생성자를 호출 할 수 있다 -&gt; 중복 제거</p>
<h3 id="this-의-규칙">this 의 규칙</h3>
<ul>
<li>this()는 생성자 코드의 첫줄에만 작성가능하다.
&lt;규칙위반&gt;<pre><code class="language-java">public MemberConstruct(String name, int age) {
System.out.println(&quot;go&quot;);
this(name, age, 50);
}</code></pre>
<h2 id="문제와풀이1">문제와풀이1</h2>
&lt;Book.java&gt;<pre><code class="language-java">package construct.ex;
</code></pre>
</li>
</ul>
<p>public class Book {
    String title;
    String author;
    int page;</p>
<pre><code>Book(String title, String author){
    this(title, author,0);
}
Book(String title, String author, int page){
    this.title = title;
    this.author=author;
    this.page = page;
}</code></pre><p>}</p>
<pre><code>&lt;BookMain.java&gt;
```java
package construct.ex;

public class BookMain {
    public static void main(String[] args) {
        Book book0 = new Book(&quot;&quot;,&quot;&quot;);
        Book book1 = new Book(&quot;Hello Java&quot;, &quot;Seo&quot;);
        Book book2 = new Book(&quot;JPA 프로그래밍&quot;, &quot;kim&quot;,700);

        Book[] books = {book0,book1,book2};
        for (Book b:books){
            System.out.println(&quot;제목: &quot;+b.title+&quot;  저자:&quot;+b.author+ &quot;  페이지:&quot;+b.page);
        }
    }
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[NestJS -4]]></title>
            <link>https://velog.io/@baby_potato/NestJS-4</link>
            <guid>https://velog.io/@baby_potato/NestJS-4</guid>
            <pubDate>Thu, 18 Apr 2024 09:19:43 GMT</pubDate>
            <description><![CDATA[<h1 id="데이터베이스와-함께-crud-구현-위해-정리할-부분">데이터베이스와 함께 CRUD 구현 위해 정리할 부분</h1>
<h2 id="데이터베이스-연동을-위해-crud-구현을-위해-수정-할-부분">데이터베이스 연동을 위해 CRUD 구현을 위해 수정 할 부분</h2>
<p>1.먼저 Service 와 Controller 파일에서 로직들을 다 수정해야 하기 때문에 
원래 있던 부분들을 주석 처리 한다.</p>
<ol start="2">
<li><p>메모리에 데이터 저장이 아니니 Service에 board 배열을 지워줍니다.</p>
</li>
<li><p>게시물 데이터를 정의하는데 Entity를 이용하기 때문에 Board Model 파일에 있는 Board Interface는 지우고   </p>
</li>
<li><p>Status Enum 은 아직도 필요하기 때문에 이 부분만을 위한 파일을 생성해서 넣어줍니다. </p>
</li>
</ol>
<ul>
<li>board.model.ts 파일을 board-status.enum.ts 으로 변경</li>
</ul>
<ol start="5">
<li>데이터베이스 이용으로 인한 불필요한 경로 지워주기</li>
</ol>
<ul>
<li>board-status-validation.pipe.ts   BoardStatus </li>
<li>boards.controller.ts</li>
<li>board.entity.ts BoardStatus</li>
<li>uuid </li>
</ul>
<h1 id="아이디를-이용해서-특정-게시물-가져오기">아이디를 이용해서 특정 게시물 가져오기</h1>
<p>이제는 메모리에서 데이터를 가져오는게 아닌 데이터베이스에서 가져오고 TypeORM을 쓸 때는 Repository 패턴을 사용한다고 했기 때문에 
Board 서비스(service)에 Board 리포지터리(Repository)를 넣어줘야한다.(종속성주입, Inject)</p>
<h2 id="service에-repository-넣어주기-repository-injection">Service에 Repository 넣어주기 (Repository Injection)</h2>
<p>&lt;board.service.ts&gt;</p>
<pre><code class="language-ts">constructor(
        @InjectRepository(BoardRepository)
        private boardRepository: BoardRepository,
        //private 을 사용하면 암묵적으로 boardRepository 인자가 프로퍼티로 선언이 된다.
    ){}</code></pre>
<p><strong>@InjectRepository</strong></p>
<ul>
<li>이 데코레이터를 이용해서 이 서비스에서 BoardRepository를 이용한다고 하고 이걸boardRepository 변수에 넣어준다. </li>
</ul>
<h2 id="service에서-getboardbyid-메소드-생성하기">Service에서 getBoardById 메소드 생성하기</h2>
<ul>
<li>typeOrm 에서 제공하는 findOne 메소드 사용</li>
<li>async await을 이용해서 데이터베이스 작업이 끝난 후 결과값을 받을 수 있게 해준다</li>
</ul>
<p>&lt;service.ts&gt;</p>
<pre><code class="language-ts">async getBoardById(id: number): Promise &lt;Board&gt;{
        const found = await this.boardRepository.findOne({ where: { id } });

        if(!found){
            throw new NotFoundException(`Can&#39;t find Voard with id ${id}`)
        }
        return found;
    }</code></pre>
<h2 id="controller-부분도-수정하기">Controller 부분도 수정하기</h2>
<p>&lt;controller.ts&gt;</p>
<pre><code class="language-ts">@Get(&#39;/:id&#39;)
    getBoardById(@Param(&#39;id&#39;) id: number): Promise&lt;Board&gt;{
        return this.boardsService.getBoardById(id);
    }</code></pre>
<p> &lt;결과&gt;
 <img src="https://velog.velcdn.com/images/baby_potato/post/7c5b99f3-b497-46ef-92bb-f841e4737ae7/image.png" alt=""></p>
<h1 id="게시물-생성하기">게시물 생성하기</h1>
<p>&lt;board.service.ts&gt;</p>
<pre><code class="language-ts">async createBoard(createBoardDto: createBoardDto): Promise&lt;Board&gt;{
        const {title, description} = createBoardDto;
        const board = this.boardRepository.create({
            title,
            description,
            status: BoardStatus.PUBLIC 
        })
        await this.boardRepository.save(board);
        return board;
    }</code></pre>
<p>&lt;board.controller.ts&gt;</p>
<pre><code class="language-ts"> @Post()
    @UsePipes(ValidationPipe)
    createBoard(@Body() createBoardDto: createBoardDto): Promise&lt;Board&gt;{
        return this.boardsService.createBoard(createBoardDto);
    }</code></pre>
<p> <img src="https://velog.velcdn.com/images/baby_potato/post/e690aac9-c26b-4af9-b42b-a5d45fb1b309/image.png" alt="">
<img src="https://velog.velcdn.com/images/baby_potato/post/67078299-3e7e-4b22-9cfd-66836feb1735/image.png" alt=""></p>
<h2 id="데이터베이스에-관련된-로직은-repository로-이동">데이터베이스에 관련된 로직은 Repository로 이동...</h2>
<p>Repository Pattern은 서비스에 있는 데이테베이스관련 로직을 Repository 쪽으로 옮기면 된다.</p>
<p>&lt;board.repository.ts&gt;</p>
<pre><code class="language-ts">import { EntityRepository, Repository } from &quot;typeorm&quot;;
import { Board } from &quot;./board.entity&quot;;
import { createBoardDto } from &quot;./dto/create-board.dto&quot;;
import { BoardStatus } from &quot;./board-status.enum&quot;;

@EntityRepository(Board)
export class BoardRepository extends Repository&lt;Board&gt;{
    async createBoard(createBoardDto: createBoardDto) : Promise&lt;Board&gt; {
        const {title, description} = createBoardDto;

        const board = this.create({
            title,
            description,
            status: BoardStatus.PUBLIC
        })

        await this.save(board);
        return board;
    }

}</code></pre>
<p>&lt;board.service.ts&gt;</p>
<pre><code class="language-ts">//게시물생성하기2
    createBoard(createBoardDto: createBoardDto): Promise&lt;Board&gt; {
        return this.boardRepository.createBoard(createBoardDto);
    }</code></pre>
<p>&lt;결과&gt;
???</p>
<h1 id="게시물-삭제하기">게시물 삭제하기</h1>
<h2 id="remove-vs-delete">remove() vs delete()</h2>
<ul>
<li><p>remove 를 사용하려면 삭제대상 아이템이 무조건 데이터베이스 안에 있어야한다. 그렇지 않으면 에러(404)</p>
</li>
<li><p>delete:  아이템이 존재하면 지우고 존재하지 않으면 아무런 영향이 없다. </p>
</li>
</ul>
<p>이러한 차이 때문에 remove를 이용하면 하나의 아이템을 지울 때 두번 데이터베이스를 이용해야한다.(아이템 유무 + 지우기) 따라서 데이터베이스에 한번만 접근해도 되는 delete 메소드를 사용해준다.</p>
<p>&lt;board.service.ts&gt;</p>
<pre><code class="language-ts">async deleteBoard(id: number): Promise&lt;void&gt; {
        const result = await this.boardRepository.delete(id);

        console.log(&#39;result&#39;, result);
    }</code></pre>
<p>&lt;board.controller.ts&gt;</p>
<pre><code class="language-ts">@Delete(&#39;/:id&#39;)
    deleteBoard(@Param(&#39;id&#39;, ParseIntPipe) id: number): Promise&lt;void&gt; {
        return this.boardsService.deleteBoard(id);
    }</code></pre>
<ul>
<li>ParseIntPipe</li>
</ul>
<p><img src="https://velog.velcdn.com/images/baby_potato/post/38168261-9ed7-49ae-87a2-0471d4e9b870/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/baby_potato/post/fbb3f97c-3a1b-4fab-8040-00121955f105/image.png" alt=""></p>
<p>delete를 사용하면 없는 id 를 지우더라고 에러가 발생하지 않는다.
=&gt; 따로 에러메세지를 출력해보자.
&lt;serviec.ts&gt;</p>
<pre><code class="language-ts">async deleteBoard(id: number): Promise&lt;void&gt; {
        const result = await this.boardRepository.delete(id);

        if(result.affected ===0){
            throw new NotFoundException(`Can&#39;t find Board with id ${id}`)
        }
        console.log(&#39;result&#39;, result);
    }
    ```
![](https://velog.velcdn.com/images/baby_potato/post/df1f4c4a-0fe1-4ec1-bd70-998325b6e808/image.png)

# 게시물 상태 업데이트하기

&lt;board.service.ts&gt;
```ts
async updateBoardStatus(id: number, status: BoardStatus): Promise&lt;Board&gt; {
        const board = await this.getBoardById(id);
        board.status = status;

        await this.boardRepository.save(board);

        return board;
    }</code></pre>
<p>&lt;board.controller.ts&gt;</p>
<pre><code class="language-ts"> @Patch(&#39;/:id/status&#39;)
    updateBoardStatus(
        @Param( &#39;id&#39;, ParseIntPipe) id: number,
        @Body(&#39;status&#39;, BoardStatusValidationPipe) status: BoardStatus,
    ): Promise&lt;Board&gt; {
        return this,this.boardsService.updateBoardStatus(id,status);
    }
    ```

 ![](https://velog.velcdn.com/images/baby_potato/post/30c8be7d-6349-4f2c-8477-bf12bd519f83/image.png)

# 모든 게시물 가져오기
&lt;board.service.ts&gt;
```ts
async getAllBoards(): Promise&lt;Board[]&gt; {
        return this.boardRepository.find();
    }</code></pre>
<p>&lt;board.controller.ts&gt;</p>
<pre><code class="language-ts">@Get(&#39;/&#39;)
    getAllBoard(): Promise&lt;Board[]&gt; {
        return this.boardsService.getAllBoards();
    }</code></pre>
<p><img src="https://velog.velcdn.com/images/baby_potato/post/8533a38b-eb7c-47ac-85f4-12c92acf35fc/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[NestJS -3]]></title>
            <link>https://velog.io/@baby_potato/NestJS-3</link>
            <guid>https://velog.io/@baby_potato/NestJS-3</guid>
            <pubDate>Mon, 15 Apr 2024 11:00:16 GMT</pubDate>
            <description><![CDATA[<h1 id="nestjs-pipes">NestJS Pipes</h1>
<h2 id="pipe">Pipe</h2>
<blockquote>
<ul>
<li>파이프는 @Injectable () 데코레이터로 주석이 달린 클래스 </li>
</ul>
</blockquote>
<ul>
<li><strong>data transformation</strong>과 <strong>data validation</strong>을 위해서 사용 </li>
<li>파이프는 컨트롤러 경로 처리기에 의해 처리되는 인수에 대해 작동</li>
</ul>
<p>Nest는 메소드가 호출되기 직전에 파이프를 삽입하고 파이프는 메소드로 향하는 인수를 수신하고 이에 대해 작동합니다.
<img src="https://velog.velcdn.com/images/baby_potato/post/5f8ef473-1e17-42aa-98dd-7a399a7e4b59/image.png" alt=""></p>
<h2 id="data-transformation">Data Transformation</h2>
<p>입력 데이터를 원하는 <strong>형식으로 변환</strong> (예 : 문자열에서 정수로)
 숫자를 받길 원하는데 문자열 형식으로 온다면 파이프에서 자동으로 숫자로 바꿔준다.
String to Integer  EX)  string &#39;7&#39;  =&gt;  Integer 7</p>
<h2 id="data-validation">Data validation</h2>
<p>입력 데이터를 평가하고 <strong>유효</strong>한 경우 변경되지 않은 상태로 전달하면 된다. 그렇지 않으면 데이터가 올바르지 않아 예외를 발생시킵니다.
만약 이름의 길이가 10자 이하여야 하는데 10자 이상 되면 에러를 발생</p>
<h2 id="pipe-사용하는-법binding-pipes">PIPE 사용하는 법(Binding Pipes)</h2>
<p>파이프를 사용하는 방법(Binding pipes)은 세가지로 나눠질수 있다.</p>
<h3 id="1-handler-level-pipes">1. Handler-level Pipes</h3>
<p> 핸들러 레벨에서 @UsePipes() 데코레이터를 이용한다.
이 파이프는 모든 파라미터에 적용된다. (title, description)</p>
<h3 id="2-parameter-level-pipes">2. Parameter-level Pipes</h3>
<p>파라미터 레벨의 파이프 이기에 특정한 파라미터에게만 적용이 되는 파이프 </p>
<h3 id="3-global-level-pipes">3. Global-level Pipes</h3>
<p>글로벌 파이프로서 애플리케이션 레벨의 파이브
클라이언트에서 들어오는 모든 요청에 적용
가장 상단 영역인 main.ts에 작성</p>
<h2 id="built-in-pipes">Built-in Pipes</h2>
<p>Nest JS 에 기본적으로 사용할 수 있게 만들어 놓은 6가지의 파이프가 있다.</p>
<ul>
<li>ValidationPipe</li>
<li>ParseIntPipe</li>
<li>ParseBoolPipe</li>
<li>ParseArrayPipe</li>
<li>ParseUUIDPipe</li>
<li>DefaultValuePipe</li>
</ul>
<h1 id="파이프를-이용한-유효성-체크">파이프를 이용한 유효성 체크</h1>
<h2 id="모듈-설치">모듈 설치</h2>
<p>class-validator , class-transformer 
npm install class-validator class-transformer --save</p>
<p>-참조 ⬇⬇
<a href="https://github.com/typestack/class-validator#manual-validation">Documentation 페이지</a></p>
<h2 id="파이프-생성하기">파이프 생성하기</h2>
<p>현재는 게시물을 생성할 때  이름과 설명에 아무런 값을 주지 않아도 아무 문제 없이 생성이 된다.=&gt; 파이프로 수정
&lt;create-board.dto.ts&gt;</p>
<pre><code class="language-ts">import { IsNotEmpty } from &quot;class-validator&quot;;

export class createBoardDto{
    @IsNotEmpty() // null 불허
    title: string;

    @IsNotEmpty()
    description: string;
}</code></pre>
<p>&lt;controller.ts&gt;</p>
<pre><code class="language-ts">@Post()
    @UsePipes(ValidationPipe) //유효성 체크
    createBoard(
        @Body() createBoardDto: createBoardDto
    ): Board { //return 값의 타입은 Board, Board[]로 주면 안됨 : service의 createBoard의 return 값이 board 하나이기 때문에
        return this.boardsService.createBoard(createBoardDto);
    }</code></pre>
<p>&lt;결과&gt;
<img src="https://velog.velcdn.com/images/baby_potato/post/5bc9a1ff-4639-4f9d-a531-be364460f9b3/image.png" alt="">
에러가 나온다!</p>
<h1 id="특정-게시물을-찾을-때-없는-경우-결과-값-처리">특정 게시물을 찾을 때 없는 경우 결과 값 처리</h1>
<p><img src="https://velog.velcdn.com/images/baby_potato/post/6114f6c7-820f-4828-8056-4e43a58b4aad/image.png" alt="">
현재 특정 게시물을 ID로 가져올 때 만약 없는 아이디의 게시물을 가져오려고 한다면 결과값으로 아무 내용이 없이 돌아온다.
=&gt; 가져오려는 내용이 없다는 결과를 클라이언트로 보내준다.
&lt;service.ts&gt; 기존의 getBoardId를 수정</p>
<pre><code class="language-ts"> getBoardId(id: string): Board{ //게시물 하나를 return 하기 때문에 Board[] X
        const found = this.boards.find((board)=&gt;board.id === id);

        if(!found){
            throw new NotFoundException(`Can&#39;t find Board with id ${id}`);
        }
        return found;
    }</code></pre>
<p>&lt;결과&gt;
<img src="https://velog.velcdn.com/images/baby_potato/post/25670c64-f3cf-47c4-b317-eb2373e77895/image.png" alt=""></p>
<h1 id="없는-게시물을-지우려-할-때-결과-값-처리">없는 게시물을 지우려 할 때 결과 값 처리</h1>
<p>&lt;service.ts&gt;의 deleteBoard를 수정</p>
<pre><code class="language-ts">deleteBoard(id: string): void{ // return 을 따로 주지 않아도 되기 때문에 void
        const found = this.getBoardId(id);
        this.boards = this.boards.filter((board) =&gt; board.id !==found.id);
    }</code></pre>
<h1 id="커스텀-파이프를-이용한-유효성-체크">커스텀 파이프를 이용한 유효성 체크</h1>
<p>지금까지는 NestJS에서 이미 구성해놓은 built-in 파이프를 사용해보았다.
따로 생성해서 사용할 수 있는 CUSTOM PIPE를 만들어보자!</p>
<h2 id="커스텀-파이프-구현-방법">커스텀 파이프 구현 방법</h2>
<pre><code>**PipeTransform이란 인터페이스**를 새롭게 만들 커스텀 파이프에 구현해줘야 한다. 
이 PipeTransform 인터페이스는 모든 파이프에서 구현해줘야 하는 인터페이스이다. 
그리고 이것과 함께 모든 파이프는 **transform()** 메소드가 필요하다. 이 메소드는 NestJS가  인자(arguments)를 처리하기 위해서 사용된다.</code></pre><p><img src="https://velog.velcdn.com/images/baby_potato/post/ccb7b447-64a7-4753-9a2e-8550da5c704b/image.png" alt="">
&lt;board-status-validation.pipe.ts&gt;</p>
<pre><code class="language-ts">import { ArgumentMetadata, PipeTransform } from &quot;@nestjs/common&quot;;

export class BoardStatusValidationPipe implements PipeTransform{
    transform(value: any, metadata: ArgumentMetadata) {
        console.log(&#39;value&#39;,value)
        console.log(&#39;metadata&#39;,metadata)

        return value;
    }
}</code></pre>
<h2 id="transform-메소드">transform() 메소드</h2>
<p>두개의 파라미터를 가지는 메소드
첫번째 파라미터는 처리가 된 인자의 값(value)이며,
두번째 파라미터는 인자에 대한 메타 데이터를 포함한 객체이다.</p>
<p>transform()메소드에서 Return 된 값은 Route 핸들러로 전해진다.
만약 예외(Exception)가 발생하면 클라이언트에 바로 전해진다. </p>
<h2 id="실제로-value-와-metadata값-콘솔로-찍어보기">실제로 value 와 metadata값 콘솔로 찍어보기</h2>
<ol>
<li>커스텀 파이프 생성 </li>
<li>게시물에 업데이트하는 핸들러에  커스텀 파이프 넣어주기 </li>
<li>포스트 맨으로 요청 보내기  </li>
</ol>
<p>&lt;controller.ts&gt;의 :id/status 부분 수정
status 를 바꿀 때 유효성 체크(status 값만!)</p>
<pre><code class="language-ts">@Patch(&#39;/:id/status&#39;)
    updateBoardStatus(
        @Param(&#39;id&#39;) id: string,
        @Body(&#39;status&#39;, BoardStatusValidationPipe) status: BoardStatus,
    ): Board {
        return this.boardsService.updateBoardStatus(id, status);
    }</code></pre>
<p>&lt;postman 호출 시&gt;
<img src="https://velog.velcdn.com/images/baby_potato/post/75199a69-b050-4b2a-8dc9-e4556cdd07e1/image.png" alt="">
&lt;terminal 콘솔&gt;
 <img src="https://velog.velcdn.com/images/baby_potato/post/498471c9-cc98-4947-b7da-963812abbd7d/image.png" alt=""></p>
<h2 id="커스텀-파이프로-실제-기능-구현하기">커스텀 파이프로 실제 기능 구현하기</h2>
<p>구현 할 기능 : 상태(Status)는  PUBLIC과 PRIVATE만 올 수 있기 때문에 이외의 값이 오면 에러 전송
&lt;board-status-validation.pipe.ts&gt;</p>
<pre><code class="language-ts">import { ArgumentMetadata, BadRequestException, PipeTransform } from &quot;@nestjs/common&quot;;
import { BoardStatus } from &quot;../board.model&quot;;

export class BoardStatusValidationPipe implements PipeTransform{
    //readonly : 클래스 외부에서 접근은 가능하지만 변경은 불가!
    readonly StatusOptions =[
        BoardStatus.PRIVATE,
        BoardStatus.PUBLIC
    ]

    transform(value: any) { // value : 사용자가 입력한 status
        value = value.toUpperCase(); //대문자 변환

        if(!this.isStatusValid(value)){
            throw new BadRequestException(`${value} isn&#39;i in the status`)
        }

        return value;
    }

    private isStatusValid(status: any){
        const index = this.StatusOptions.indexOf(status); //배열안에 있는지, 있으면 그 배열내 인덱스값
        return index !== -1;
    }
}</code></pre>
<p>&lt;결과1&gt;
<img src="https://velog.velcdn.com/images/baby_potato/post/946f6f77-6bc4-4a69-8853-cf32cbf89c20/image.png" alt="">
&lt;결과2&gt;
<img src="https://velog.velcdn.com/images/baby_potato/post/bbdd05aa-bf92-412b-8630-635fc0c0191a/image.png" alt=""></p>
<h1 id="postgressql-설치하기">PostgresSQL 설치하기</h1>
<p>애플리케이션에 데이터베이스를 연결해서 데이터베이스에 보관, 데이터베이스는 Postgres를 사용</p>
<p>&lt;설치 목록&gt;</p>
<ol>
<li>PostgresSQL</li>
<li>pgAdmin  (데이터베이스를 보는 툴(Tool)입니다.) </li>
</ol>
<p>pgAdmin 실행</p>
<ol>
<li>Servers &gt; register Server
<img src="https://velog.velcdn.com/images/baby_potato/post/984d99d3-3df4-4741-9c1a-470a7bf41bf2/image.png" alt="">
<img src="https://velog.velcdn.com/images/baby_potato/post/33d86543-7795-485d-863f-58c7942289d2/image.png" alt=""></li>
<li>BoardProject 우클릭 &gt; Create &gt; database
<img src="https://velog.velcdn.com/images/baby_potato/post/16afddef-cbce-4453-ada4-20d23aec6d10/image.png" alt=""></li>
</ol>
<h1 id="typeorm-object-relational-mapping소개">TypeORM (Object Relational Mapping)소개</h1>
<h2 id="typeorm">TypeORM</h2>
<blockquote>
<p>TypeORM은 node.js에서 실행되고 TypeScript로 작성된 객체 관계형 매퍼 라이브러리</p>
</blockquote>
<p>TypeORM은 MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server, Oracle, SAP Hana 및 WebSQL과 같은 여러 데이터베이스를 지원한다. </p>
<h2 id="orm-object-relational-mapping-이란">ORM (Object Relational Mapping) 이란?</h2>
<p>객체와 관계형 데이터베이스의 데이터를 자동으로 변형 및 연결하는 작업</p>
<p>ORM을 이용한 개발은 객체와 데이터베이스의 변형에 유연하게 사용할 수 있다.
<img src="https://velog.velcdn.com/images/baby_potato/post/5f36527d-04f8-48c2-b8e7-c97efb0dd7f9/image.png" alt=""></p>
<h2 id="typeorm-vs-pure-javascript">TypeORM vs Pure Javascript</h2>
<pre><code>const boards = Board.find({ title: &#39;Hello&#39; , status: &#39;PUBLIC&#39; });</code></pre><pre><code>db.query(&#39;SELECT * FROM boards WHERE title = &quot;Hello&quot; AND status = &quot;PUBLIC&quot; , (err, result) =&gt; {
       if(err) { 
          throw new Error(&#39;Error&#39;)
      }
      boards = result.rows;
})
</code></pre><h2 id="typeorm-특징과-이점">TypeORM 특징과 이점</h2>
<ul>
<li>모델을 기반으로 데이터베이스 테이블 체계를 자동으로 생성</li>
<li>데이터베이스에서 개체를 쉽게 삽입, 업데이트 및 삭제할 수 있다.</li>
<li>테이블 간의 매핑 (일대일, 일대 다 및 다 대다)을 만든다.</li>
<li>간단한 CLI 명령을 제공</li>
<li>TypeORM은 간단한 코딩으로 ORM 프레임 워크를 사용하기 쉽다. </li>
<li>TypeORM은 다른 모듈과 쉽게 통합된다.</li>
</ul>
<h1 id="typeorm-애플리케이션에서-이용하기">TypeORM 애플리케이션에서 이용하기</h1>
<h2 id="typeorm을-사용하기-위해서-설치해야하는-모듈들">TypeORM을 사용하기 위해서 설치해야하는 모듈들</h2>
<p>*<em>@nestjs/typeorm *</em></p>
<ul>
<li>NestJS에서 TypeOrm을 사용하기 위해 연동시켜주는 모듈</li>
</ul>
<p>*<em>typeorm *</em></p>
<ul>
<li>TypeORM 모듈 </li>
</ul>
<p><strong>pg</strong></p>
<ul>
<li>Postgres 모듈</li>
</ul>
<pre><code>npm install pg typeorm @nestjs/typeorm --save </code></pre><p><a href="https://docs.nestjs.com/techniques/database">database DOC</a></p>
<h2 id="typeorm--애플리케이션에-연결하기">TypeORM  애플리케이션에 연결하기</h2>
<p><img src="https://velog.velcdn.com/images/baby_potato/post/88b9d779-ce0b-4e7e-a342-8ef13592b99f/image.png" alt="">
&lt;typeorm.config.ts&gt;</p>
<pre><code class="language-ts">import { TypeOrmModuleOptions } from &quot;@nestjs/typeorm&quot;;

export const typeORMConfig : TypeOrmModuleOptions = {
    //Database Type
    type: &#39;postgres&#39;,
    host: &#39;localhost&#39;,
    port: 5432,
    username:&#39;postgres&#39;,
    password: &#39;0121&#39;,
    database: &#39;board-app&#39;,
    //엔티티는 하나씩 넣을 수도 있지만 이렇게 다 포함하게 할 수 있다. 
    entities: [__dirname+&#39;/../**/*.entity.{js,ts}&#39;],
    synchronize: true 

}</code></pre>
<ul>
<li>synchronize: true 일때 : 애플리케이션을 다시 실행할 때 엔티티안에서 수정된 컬럼의 길이 타입 변경 값등을 해당 테이블을 Drop 후 다시 생성해줌 </li>
</ul>
<p>&lt;app.module.ts&gt; -typeOrm 사용 설정</p>
<pre><code class="language-ts">import { Module } from &#39;@nestjs/common&#39;;

import { BoardsModule } from &#39;./boards/boards.module&#39;;
import { TypeOrmModule } from &#39;@nestjs/typeorm&#39;;
import { typeORMConfig } from &#39;./configs/typeorm.config&#39;;

@Module({
  imports: [
    TypeOrmModule.forRoot(typeORMConfig),
    BoardsModule],

})
export class AppModule {}</code></pre>
<ul>
<li>forRoot안에 넣어준 설정(configuration)은 모든 Sub-Module 부수적인 모듈들에 다 적용이 된다.</li>
</ul>
<h1 id="게시물을-위한-엔티티entity-생성하기">게시물을 위한 엔티티(Entity) 생성하기</h1>
<h2 id="왜-entity를-생성해야하나">왜 Entity를 생성해야하나?</h2>
<p>원래 ORM 없이 데이터베이스 테이블을 생성할 때는 아래와 같다.</p>
<pre><code class="language-sql">CREATE TABLE board ( 
   id     INTEGER AUTO_INCREMENT PRIMARY KEY. 
   title  VARCHAR(255)  NOT NULL,
   decsription VARCHAR(255) NOT NULL  
)</code></pre>
<p>하지만 TypeORM을 사용할 때는 Class를 사용해서 데이터베이스 테이블로 변환시켜주기 때문에 클래스를 생성한 후 그 안에 컬럼들을 정의해주면 된다. </p>
<h2 id="엔티티-생성-코드">엔티티 생성 코드</h2>
<p><img src="https://velog.velcdn.com/images/baby_potato/post/6ea70f7e-8e77-4c0f-87f7-50d3e6bea615/image.png" alt="">
&lt;board.entity.ts&gt;</p>
<pre><code class="language-ts"></code></pre>
<h3 id="entity">@Entity()</h3>
<p>Entity () 데코레이터 클래스는 Board 클래스가 엔티티임을 나타내는 데 사용 (sql 문의 CREATE TABLE board 부분)</p>
<h3 id="primarygeneratedcolumn">@PrimaryGeneratedColumn()</h3>
<p>PrimaryGeneratedColumn () 데코레이터 클래스는 id 열이 Board 엔터티의 기본 키 열임을 나타내는 데 사용</p>
<h3 id="column">@Column()</h3>
<p>Column () 데코레이터 클래스는 Board 엔터티의 title 및 description과 같은 다른 열을 나타내는 데 사용</p>
<h1 id="repository-생성하기">Repository 생성하기</h1>
<h2 id="repository">Repository</h2>
<p>리포지토리는 엔터티 개체와 함께 작동하며 엔티티 찾기, 삽입, 업데이트, 삭제 등을 처리
<a href="http://typeorm.delightful.studio/classes/_repository_repository_.repository.html">Repository란?</a>
<img src="https://velog.velcdn.com/images/baby_potato/post/40397e56-8c3c-4c12-821e-14110748a84a/image.png" alt="">
데이터베이스에 관련 된 일(INSERT, FIND, DELETE..등등)은 서비스에서 하는게 아닌 Repository에서 해주면 된다.
이것을 <strong>Repository Pattern</strong> 이라고도 한다.</p>
<h2 id="repository-생성하기-1">Repository 생성하기</h2>
<ol>
<li><p>리포지토리 파일 생성하기 
<img src="https://velog.velcdn.com/images/baby_potato/post/f30c9e58-5ab9-40c4-87f0-04e44d07adb6/image.png" alt=""></p>
</li>
<li><p>생성한 파일에 리포지토리를 위한 클래스 생성하기 (BoardRepository)
&lt;board.repository.ts&gt;</p>
<pre><code class="language-ts">import { EntityRepository, Repository } from &quot;typeorm&quot;;
import { Board } from &quot;./board.entity&quot;;
</code></pre>
</li>
</ol>
<p>@EntityRepository(Board)
export class BoardRepository extends Repository<Board>{</p>
<p>}</p>
<pre><code>- 생성 시 Repository 클래스를 Extends 해줍니다. (Find, Insert, Delete 등 엔티티를 컨트롤 해줄 수 있습니다.) 

@EntityRepository()
- 클래스를 사용자 정의(CUSTOM) 저장소로 선언하는 데 사용
사용자 지정 저장소는 일부 특정 엔터티를 관리하거나 일반 저장소 일 수 있다. 

3. 생성한 Repository를 다른곳에서도 사용할 수 있기 위해서(Injectable) board.module에서 import 해준다. 
&lt; board.module.ts&gt;
```ts
import { Module } from &#39;@nestjs/common&#39;;
import { BoardsController } from &#39;./boards.controller&#39;;
import { BoardsService } from &#39;./boards.service&#39;;
import { TypeOrmModule } from &#39;@nestjs/typeorm&#39;;
import { BoardRepository } from &#39;./board.repository&#39;;

@Module({
  imports:[TypeOrmModule.forFeature([BoardRepository])],
  controllers: [BoardsController],
  providers: [BoardsService]
})
export class BoardsModule {}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[NestJS -2]]></title>
            <link>https://velog.io/@baby_potato/NestJS-2</link>
            <guid>https://velog.io/@baby_potato/NestJS-2</guid>
            <pubDate>Sat, 13 Apr 2024 10:35:42 GMT</pubDate>
            <description><![CDATA[<p>시작 전 PC 가 바뀌어 새로운 PC에 프로젝트를 설정해야 했다.
git에 nestjs-board-app을 업로드 해놓아서 gitclone 으로 프로젝트를 열고
cmd 에 &#39;nest i -g @nest/cli&#39; 실행 후 프로젝트를 열어보니 main.ts 에 오류가 떠 있었다..</p>
<blockquote>
<p>Cannot find module &#39;@nestjs/core&#39; or its corresponding type declarations.</p>
</blockquote>
<p>라는 오류였고, 쉽게 해결 할 수 있었다.
terminal 에 &#39;npm install @nestjs/core&#39;와 &#39;npm install @nestjs/common&#39; 을 입력하여 해결하였다.</p>
<h1 id="모든-게시물을-가져오는-서비스-만들기-crud--r">모든 게시물을 가져오는 서비스 만들기 (CRUD =&gt; R)</h1>
<h2 id="모든-게시물-데이터를-데이터베이스에서-가져오는-로직-구현">모든 게시물 데이터를 데이터베이스에서 가져오는 로직 구현</h2>
<p>로직은 Service에서 구현. 바로 데이터베이스와 연동해서 하면 되지만 처음부터 데이터베이스 연결해서 하면 헷갈릴수있기 때문에 우선 데이터를 로컬 메모리에 담아서 처리
&lt;board.service.ts&gt;</p>
<pre><code class="language-ts"> import { Injectable } from &#39;@nestjs/common&#39;;

@Injectable()
export class BoardsService {
    private boards =[];

    getAllBoards(){
        return this.boards;
    }
}</code></pre>
<blockquote>
<p>private boards의 private 사용 이유 : 사용하지 않으면 다른 컴포넌트에서 이 BoardsService에 접근해서 이 boards 배열 값을 수정할 수 있기 때문</p>
</blockquote>
<p>&lt;board.controller.ts&gt;</p>
<pre><code class="language-ts">import { Controller, Get } from &#39;@nestjs/common&#39;;
import { BoardsService } from &#39;./boards.service&#39;;

@Controller(&#39;boards&#39;)
export class BoardsController {
    constructor(private boardService: BoardsService) {}

    @Get()
    getAllBoard() {
        return this.boardService.getAllBoards;
    }
}</code></pre>
<p>&#39;npm run start:dev&#39; 로 실행, localhost:3000/boards 확인
<img src="https://velog.velcdn.com/images/baby_potato/post/b5e8a07b-c1ea-4823-a80b-1c58fcbc2134/image.png" alt="">
=&gt; 클라이언트에서 요청을 보내면 먼저 컨트롤러로 가며 컨트롤러에서 알맞은 요청 경로에 
라우팅해서 해당 핸들러로 가게 한다. 그런 후에 요청을 처리해주기 위해서 서비스로 들어가며 그 요청에 맞는 로직을 서비스에서 처리해준 후 컨트롤러에 리턴값을 보내준후 컨트롤러에서 클라이언트로 결과값을 보내준다. 
*<em>컨트롤러에서는 요청을 처리하고 결과값을 리턴해주는 역할을 한다. *</em></p>
<h1 id="board-model-정의하기">Board Model 정의하기</h1>
<p>게시물 데이터에는 ID가 필요하고 이름이나 설명등이 필요 하다는 정의를 토대로 시작!
<img src="https://velog.velcdn.com/images/baby_potato/post/f78c884e-2dd4-4d30-876f-3ed08b88b956/image.png" alt=""></p>
<h2 id="모델을-정의하기-위해서">모델을 정의하기 위해서</h2>
<p>Class를 이용하거나 Interface 이용 </p>
<ul>
<li>Interface : 변수의 타입만 체크</li>
<li>Classes : 변수의 타입, 인스턴스 또한 생성 가능</li>
</ul>
<p><strong>우선 Interface 사용</strong></p>
<ul>
<li>status : 게시글이 공개인지 비공개인지 나눠주는 것
  두가지 상태 이외의 것은 나오면 안되기 때문에 타입스크립스의 기능인 <strong>enumeration</strong>을 사용</li>
</ul>
<p>&lt;board.model.ts&gt;</p>
<pre><code class="language-ts">export interface Board{
    id : string,
    title : string,
    dedcription: string,
    status: BoardStatus; //PUBLIC or PRIVATE
}

export enum BoardStatus {
    PUBLIC = &#39;PUBLIC&#39;,
    PRIVATE =&#39;PRIVATE&#39; 
}</code></pre>
<p>생성한 Board 모델을 이용해서 타입을 정의 ⬇
&lt;boards.service.ts&gt;</p>
<pre><code class="language-ts">import { Injectable } from &#39;@nestjs/common&#39;;
import { Board } from &#39;./board.model&#39;;

@Injectable()
export class BoardsService {
    private boards: Board[] = []; // 배열주의

    getAllBoards(): Board[] { //배열주의
        return this.boards;
    }
}</code></pre>
<p>&lt;boards.controller.ts&gt;</p>
<pre><code class="language-ts">import { Controller, Get } from &#39;@nestjs/common&#39;;
import { BoardsService } from &#39;./boards.service&#39;;
import { Board } from &#39;./board.model&#39;;

@Controller(&#39;boards&#39;)
export class BoardsController {
    constructor(private boardsService: BoardsService) {}

    @Get(&#39;/&#39;)
    getAllBoard(): Board[] { //배열주의
        return this.boardsService.getAllBoards();
    }
}</code></pre>
<h2 id="타입을-정의하면-좋은-이유">타입을 정의하면 좋은 이유</h2>
<ol>
<li>타입을 정의해주므로서 원하는 타입과 다른 코드를 사용할 시 에러가 발생</li>
<li>코드를 읽는 입장에서 더 코드를 쉽게 이해하며 읽을 수 있다. (readable)</li>
</ol>
<h1 id="게시물-생성하기-crud--c--service">게시물 생성하기 (CRUD =&gt; C) : Service</h1>
<h2 id="게시물-생성-기능-만들기">게시물 생성 기능 만들기</h2>
<p>게시물에 관한 로직을 처리하는 부분은 Service , Service 에서 처리 후 Controller에서 서비스를 호출</p>
<p>id 는 모든 게시물에 유니크 해야한다. 만약 데이터베이스에 데이터를 넣어주는 경우라면 데이터베이스에서 알아서 유니크한 값을 주지만 지금은 임의로 값을 주어야함 
=&gt; <strong>uuid 모듈</strong>을 이용</p>
<h2 id="uuid-모듈">uuid 모듈</h2>
<pre><code>npm install uuid --save</code></pre><p>service 에 &#39;import { v1 as uuid } from &#39;uuid&#39;;&#39; 추가</p>
<p>&lt;boards.service.ts&gt;</p>
<pre><code class="language-ts">import { Injectable } from &#39;@nestjs/common&#39;;
import { Board, BoardStatus } from &#39;./board.model&#39;;
import { v1 as uuid } from &#39;uuid&#39;;

@Injectable()
export class BoardsService {
    private boards: Board[] = [];

    getAllBoards(): Board[] {
        return this.boards;
    }

    createBoard(title: string, description: string){
        const board: Board={
            id: uuid(),
            //title: title -&gt; 그냥 title로 생략가능 : 앞뒤 변수명이 동일
            title,
            // description: description
            description,
            status: BoardStatus.PUBLIC
        }

        //게시판에 게시글 생성한 것을 넣어주기
        this.boards.push(board);
        return board;
    }
}</code></pre>
<h1 id="게시물-생성하기-crud--c--controller">게시물 생성하기 (CRUD =&gt; C) : Controller</h1>
<h2 id="request-response">Request, Response</h2>
<p>controller 에 정의</p>
<h2 id="클라이언트에서-보내온-값을-핸들러에-가져오기">클라이언트에서 보내온 값을 핸들러에 가져오기</h2>
<p>클라이언트에서의 정보 받아오기
=&gt; @Body body ..
&lt;boards.controller.ts&gt;</p>
<pre><code class="language-ts">import { Body, Controller, Get, Post } from &#39;@nestjs/common&#39;;
import { BoardsService } from &#39;./boards.service&#39;;
import { Board } from &#39;./board.model&#39;;

@Controller(&#39;boards&#39;)
export class BoardsController {
    constructor(private boardsService: BoardsService) {}

    @Get(&#39;/&#39;)
    getAllBoard(): Board[] {
        return this.boardsService.getAllBoards();
    }

    //body 전체 한번에 가져오기
    // @Post()
    // createBoard(@Body() body) {
    //     console.log(&#39;body&#39;, body);
    // }

    //body 의 특정 값만 가져오기
    // @Post()
    // createBoard(@Body(&#39;title&#39;) title: string, @Body(&#39;description&#39;) description: string) {
    //     console.log(&#39;title&#39;, title);
    //     console.log(&#39;description&#39;, description);
    // }

    @Post()
    createBoard(@Body(&#39;title&#39;) title: string, @Body(&#39;description&#39;) description: string): Board { //return 값의 타입은 Board, Board[]로 주면 안됨 : service의 createBoard의 return 값이 board 하나이기 때문에
        return this.boardsService.createBoard(title, description);
    }

}</code></pre>
<p><strong>위의 주석 중요!!</strong></p>
<h2 id="postman-으로-테스트">POSTMAN 으로 테스트</h2>
<p><img src="https://velog.velcdn.com/images/baby_potato/post/38c65cc7-0fff-4ecb-a059-a06ceb9e7d62/image.png" alt=""></p>
<h1 id="data-transfer-objectdto">Data transfer Object(DTO)</h1>
<h2 id="dto-data-transfer-object">DTO (Data Transfer Object)</h2>
<blockquote>
<ul>
<li>계층간 데이터 교환을 위한 객체</li>
</ul>
</blockquote>
<ul>
<li>DB에서 데이터를 얻어 Service나 Controller 등으로 보낼 때 사용하는 객체</li>
<li>DTO는 데이터가 네트워크를 통해 전송되는 방법을 정의하는 객체</li>
<li>interface나 class를 이용해서 정의 될 수 있다. (하지만 클래스를 이용하는것을 Nest JS에서는 추천)</li>
</ul>
<h2 id="dto를-쓰는-이유">DTO를 쓰는 이유</h2>
<ul>
<li>데이터 유효성을 체크하는데 효율적</li>
<li>더 안정적인 코드로 만들어 준다. </li>
<li>타입스크립트의 타입으로도 사용된다.</li>
</ul>
<p>프로퍼티(title, description...)가 많을 때 프로퍼티의 이름을 바꿔야한다면 controller, service 등 여러 곳에서 바꿔줘야한다.
=&gt; 유지보수가 힘듦 =&gt; DTO 사용</p>
<h1 id="게시물-생성을-위한-dto">게시물 생성을 위한 DTO</h1>
<h2 id="dto-파일-작성">DTO 파일 작성</h2>
<p><img src="https://velog.velcdn.com/images/baby_potato/post/80e38738-49e7-4f20-89d5-1b4281864e3b/image.png" alt=""></p>
<p>클래스는 인터페이스와 다르게 런타임에서 작동하기 때문에 파이프 같은 기능을 이용할 때 더 유용하다.
=&gt; 클래스를 사용해서 DTO를 작성
&lt;create-board.dto.ts&gt;</p>
<pre><code class="language-ts">export class createBoardDto{
    title: string;
    description: string;
}</code></pre>
<h2 id="dto-적용하기">DTO 적용하기</h2>
<p>&lt;boards.controller.ts&gt;</p>
<pre><code class="language-ts">import { Body, Controller, Get, Post } from &#39;@nestjs/common&#39;;
import { BoardsService } from &#39;./boards.service&#39;;
import { Board } from &#39;./board.model&#39;;
import { createBoardDto } from &#39;./dto/create-board.dto&#39;;

@Controller(&#39;boards&#39;)
export class BoardsController {
    constructor(private boardsService: BoardsService) {}

    @Get(&#39;/&#39;)
    getAllBoard(): Board[] {
        return this.boardsService.getAllBoards();
    }

    //body 전체 한번에 가져오기
    // @Post()
    // createBoard(@Body() body) {
    //     console.log(&#39;body&#39;, body);
    // }

    //body 의 특정 값만 가져오기
    // @Post()
    // createBoard(@Body(&#39;title&#39;) title: string, @Body(&#39;description&#39;) description: string) {
    //     console.log(&#39;title&#39;, title);
    //     console.log(&#39;description&#39;, description);
    // }

    @Post()
    createBoard(
        @Body() createBoardDto: createBoardDto
    ): Board { //return 값의 타입은 Board, Board[]로 주면 안됨 : service의 createBoard의 return 값이 board 하나이기 때문에
        return this.boardsService.createBoard(createBoardDto);
    }

}</code></pre>
<p>&lt;boards.service.ts&gt;</p>
<pre><code class="language-ts">import { Injectable } from &#39;@nestjs/common&#39;;
import { Board, BoardStatus } from &#39;./board.model&#39;;
import { v1 as uuid } from &#39;uuid&#39;;
import { createBoardDto } from &#39;./dto/create-board.dto&#39;;

@Injectable()
export class BoardsService {
    private boards: Board[] = [];

    getAllBoards(): Board[] {
        return this.boards;
    }

    createBoard(createBoardDto: createBoardDto){
        // const title = createBoardDto.title;
        // const description = createBoardDto.description;
        const { title, description }= createBoardDto;
        const board: Board={
            id: uuid(),
            //title: title -&gt; 그냥 title로 생략가능 : 앞뒤 변수명이 동일
            title,
            // description: description
            description,
            status: BoardStatus.PUBLIC
        }

        //게시판에 게시글 생성한 것을 넣어주기
        this.boards.push(board);
        return board;
    }
}</code></pre>
<h1 id="id로-특정-게시물-가져오기">ID로 특정 게시물 가져오기</h1>
<p>service -&gt; controller</p>
<p>&lt;service.ts&gt;</p>
<pre><code class="language-ts">getBoardIs(id: string): Board{ //게시물 하나를 return 하기 때문에 Board[] X
        return this.boards.find((board)=&gt;board.id === id);
    }</code></pre>
<p>&lt;controller.ts&gt;</p>
<pre><code class="language-ts">@Get(&#39;/:id&#39;)
    getBoardById(@Param(&#39;id&#39;) id: string): Board{
        return this.boardsService.getBoardId(id);
    }</code></pre>
<h2 id="param">Param</h2>
<pre><code class="language-ts">//ex) localhost:3000?id=11111 일 때는 
findOne(@Param(&#39;id&#39;) id: string)
//ex) localhost:3000?id=11111&amp;title=abcd 일 때는 
findOne(@Param() params: string[])</code></pre>
<h1 id="id로-특정-게시물-지우기">ID로 특정 게시물 지우기</h1>
<p>service -&gt; controller</p>
<p>&lt;service.ts&gt;</p>
<pre><code class="language-ts">deleteBoard(id: string): void{ // return 을 따로 주지 않아도 되기 때문에 void
        this.boards = this.boards.filter((board) =&gt; board.id !==id);
    }</code></pre>
<p>&lt;controller.ts&gt;</p>
<pre><code class="language-ts">@Delete(&#39;/:id&#39;)
    deleteBoard(@Param(&#39;id&#39;) id: string): void {
        this.boardsService.deleteBoard(id);
    }</code></pre>
<h1 id="특정-게시물의-상태-업데이트">특정 게시물의 상태 업데이트</h1>
<p>특정 게시물의 상태(PUBLIC 또는 PRIVATE)을 업데이트 해주는 기능
&lt;service.ts&gt;</p>
<pre><code class="language-ts">updateBoardStatus(id:string, status: BoardStatus):Board {
        const board = this.getBoardId(id);
        board.status = status;
        return board;
    }</code></pre>
<p>&lt;controller.ts&gt;</p>
<pre><code class="language-ts">@Patch(&#39;/:id/status&#39;)
    updateBoardStatus(
        @Param(&#39;id&#39;) id: string,
        @Body(&#39;status&#39;) status: BoardStatus
    ) {
        return this.boardsService.updateBoardStatus(id, status);
    }</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[NestJS -1]]></title>
            <link>https://velog.io/@baby_potato/NestJS</link>
            <guid>https://velog.io/@baby_potato/NestJS</guid>
            <pubDate>Thu, 11 Apr 2024 10:37:21 GMT</pubDate>
            <description><![CDATA[<h1 id="nestjs-란">NestJS 란?</h1>
<p>NestJS는 효율적이고 확장가능한 Node.js 어플리케이션을 구축하기 위한 프레임워크이다.</p>
<p>내부적으로 Nest는 Express (기본값)와 같은 강력한 HTTP 서버 프레임 워크를
사용하며 선택적으로 Fastify를 사용하도록 구성 할 수도 있다.</p>
<h1 id="nest-js-cli-설치">Nest JS CLI 설치</h1>
<p><img src="https://velog.velcdn.com/images/baby_potato/post/fc30a8c3-1d87-42f2-81a5-ed052fd47042/image.png" alt="">
<img src="https://velog.velcdn.com/images/baby_potato/post/44660ea2-68fe-4b37-a8f1-93395ece650d/image.png" alt=""></p>
<h1 id="게시물-crud-어플리케이션-소개">게시물 CRUD 어플리케이션 소개</h1>
<p><img src="https://velog.velcdn.com/images/baby_potato/post/837c7d04-e840-4aeb-aed8-5d7ddfbabfba/image.png" alt="">
이러한 어플리케이션을 제작할 예정이다.
<img src="https://velog.velcdn.com/images/baby_potato/post/94ecfb80-d276-4307-8cfd-b2aa8bac9a6f/image.png" alt=""></p>
<ul>
<li>게시글에 관한 모듈(BoardModule)</li>
<li>게시글을 만드는 사람에 대한 인증 모듈(AuthModule)</li>
</ul>
<h1 id="프로젝트-시작하기">프로젝트 시작하기</h1>
<h2 id="프로젝트-생성">프로젝트 생성</h2>
<ol>
<li>원하는 디렉토리 내에 프로젝트 폴더 생성<pre><code>mkdir nestjs-board-app</code></pre></li>
<li>프로젝트 폴더로 이동 후 <pre><code>nest new ./  입력
npm 선택</code></pre></li>
<li>vscode 로 프로젝트 폴더 열기</li>
</ol>
<h2 id="nestjs-기본-구조-설명">NestJS 기본 구조 설명</h2>
<p><img src="https://velog.velcdn.com/images/baby_potato/post/40d84634-224b-44f9-a6e0-ae701bd99be1/image.png" alt=""></p>
<ul>
<li>eslintrc.js <ul>
<li>규칙을 가지고 코드를 깔끔하게 짤수있게 도와주는 라이브러리</li>
<li>타입스크립트 가이드라인 제시, 문법에 오류가 나면 알려주는 역할</li>
</ul>
</li>
<li>.prettierrc<ul>
<li>코드의 형식을 맞춰준다.</li>
<li>작은따옴표(&#39;)를 사용할지 큰 따옴표(&quot;)를 사용할지,Indent 값을 2로 줄지 4로 줄지 등등</li>
<li>에러를 찾는 것이 아닌 코드 포멧터 역할(eslintrc.js와 차이점)</li>
</ul>
</li>
<li>nest-cli.json<ul>
<li>nest 프로젝트를 위해 특정한 설정을 할 수 있는 json 파일</li>
</ul>
</li>
<li>tsconfig.json</li>
<li>tsconfig.build.json</li>
<li>package.json<ul>
<li>프로젝트 이름, 작성자 등등</li>
<li>라이브러리 목록, 명령어 목록 등등</li>
</ul>
</li>
<li>src <ul>
<li>프로젝트를 위한 로직들이 들어가있음</li>
<li>main.ts : 앱을 생성하고 실행, 시작점..</li>
<li>app.module.ts : 앱 모듈을 정의</li>
</ul>
</li>
</ul>
<p>실행해보고 싶다면 터미널에</p>
<pre><code>npm run start:dev
#start:dev 는 package.json에 나옴</code></pre><p>&lt;결과&gt;
main.ts 의 
await app.listen(3000); =&gt; 3000번 포트이기 때문에
<img src="https://velog.velcdn.com/images/baby_potato/post/871a21a7-7c62-442a-80ca-578f3cfda0bd/image.png" alt=""></p>
<h1 id="기본-구조에서-살펴보는-nestjs-로직-흐름">기본 구조에서 살펴보는 NestJS 로직 흐름</h1>
<h2 id="hello-world-텍스트를-출력하는-api">Hello World 텍스트를 출력하는 API</h2>
<p>main.ts의 </p>
<pre><code> const app = await NestFactory.create(AppModule);</code></pre><p> AppModule에 가면
 <img src="https://velog.velcdn.com/images/baby_potato/post/cf402823-2a74-43b5-b19b-51b8fdeca5e8/image.png" alt="">
AppController, AppService 가 등록이 되어있다.</p>
<p>클라이언트(브라우저)에서 서버(nestjs-app)로 요청을 주면 app.controller 로 온다.
<img src="https://velog.velcdn.com/images/baby_potato/post/4a69deeb-37b6-4080-bea3-1bff3d65672f/image.png" alt="">
Get 메서드로 실행, app.service 를 return
<img src="https://velog.velcdn.com/images/baby_potato/post/eca5ab84-18ff-4c8d-9510-87ddeb993a42/image.png" alt="">
getHello 실행하여 브라우저로 return
<img src="https://velog.velcdn.com/images/baby_potato/post/63a8745e-ed5c-4ccd-85e2-0c5b92604afb/image.png" alt=""></p>
<h1 id="nestjs-모듈이란">NestJS 모듈이란?</h1>
<h2 id="nestjs를-사용해서-만드는-앱-구조">NestJS를 사용해서 만드는 앱 구조</h2>
<p>App Module안에 BoardModule과 AuthModule이 있으면 각 모듈안에 Controller Entity Service등이 있다.</p>
<h2 id="nestjs">NestJS?</h2>
<p>모듈은 @Module () 데코레이터로 주석이 달린 클래스다. @Module () 데코레이터는 Nest가 애플리케이션 구조를 구성하는 데 사용하는 메타 데이터를 제공한다.
각 응용 프로그램에는 <strong>하나 이상의 모듈 (루트 모듈)</strong> 이 있다. 루트 모듈은 Nest가 사용하는 시작점이다.
모듈은 기본적으로 싱글 톤이므로 여러 모듈간에 쉽게 공급자의 동일한 인스턴스를 공유 할 수 있다. =&gt; 어떠한 모듈 하나를 만들면 여러 모듈에서 사용이 가능한다.(밑 이미지)
<img src="https://velog.velcdn.com/images/baby_potato/post/371d1ad9-dab6-491a-a887-5120fcece438/image.png" alt=""></p>
<h1 id="board-module-생성하기">Board Module 생성하기</h1>
<p><img src="https://velog.velcdn.com/images/baby_potato/post/e8a08a8d-e784-4212-878e-c71af30f5960/image.png" alt=""></p>
<p>우선 src 에 app.service, appcontroller, app.spec 파일 삭제, app.moule의 내용 수정,
test 폴더 삭제
<img src="https://velog.velcdn.com/images/baby_potato/post/5885d979-bdea-4515-aef2-9d7cde88733a/image.png" alt="">
&lt;app.module.ts&gt;</p>
<pre><code class="language-ts">import { Module } from &#39;@nestjs/common&#39;;

@Module({
  imports: [],

})
export class AppModule {}</code></pre>
<h2 id="모듈-생성하기">모듈 생성하기</h2>
<pre><code>nest g module boards</code></pre><p>nest: using nestcli
g: generate
module: schematic that i want to create 
boards: name of the schematic </p>
<blockquote>
<p>위 명령어를 입력하니 nest.ps1 파일을 로드할 수 없다는 에러가 발생해서
Window PowerShell을 관리자 권한으로 연 후</p>
</blockquote>
<pre><code>get-ExecutionPolicy
=&gt; Restricted 확인</code></pre><pre><code>Set-ExecutionPolicy Unrestricted
Y 입력</code></pre><pre><code>get-ExecutionPolicy
=&gt; Unrestricted 확인</code></pre><p>위의 과정을 하고 실행하니 정상적으로 되었다.
boards 폴더 생성확인 !
<img src="https://velog.velcdn.com/images/baby_potato/post/6046986e-c695-4841-bf24-5f5e400c9dff/image.png" alt="">
BoardsModule import 확인 !
&lt;app.module.ts&gt;</p>
<pre><code class="language-ts">import { Module } from &#39;@nestjs/common&#39;;
import { BoardsModule } from &#39;./boards/boards.module&#39;;
@Module({
  imports: [BoardsModule],
})
export class AppModule {}</code></pre>
<h1 id="nestjs-controllers-이란">NestJS Controllers 이란?</h1>
<h2 id="controller란">Controller란?</h2>
<p>컨트롤러는 들어오는 요청을 처리하고 클라이언트에 응답을 반환한다.
<img src="https://velog.velcdn.com/images/baby_potato/post/1fbae918-4fb8-453f-8bd6-73269a69c7b4/image.png" alt=""></p>
<blockquote>
<p><strong>@Controller</strong> 데코레이터로 클래스를 데코레이션하여 정의된다.</p>
</blockquote>
<h2 id="handler-란-">Handler 란 ?</h2>
<p>핸들러는 <strong>@Get, @Post, @Delete</strong> 등과 같은 데코레이터로 장식된 컨트롤러 클래스 내의 단순한 메서드이다.
<img src="https://velog.velcdn.com/images/baby_potato/post/75983821-b59c-4a89-b92e-facd1c2cf67a/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/baby_potato/post/47ca0556-2d53-43d2-8de6-3961fb2baf49/image.png" alt=""></p>
<h1 id="boards-controller-생성하기">Boards Controller 생성하기</h1>
<pre><code>nest g controller boards --no-spec
=&gt; boards.controller.ts 생성</code></pre><p>nest: using nestcli
g: generate
controller: controller schematic 
boards: name of the schematic 
--no-spec: 테스트를 위한 소스 코드 생성 x </p>
<h2 id="cli로-명령어-입력-시-controller-만드는-순서">CLI로 명령어 입력 시 Controller 만드는 순서</h2>
<p><img src="https://velog.velcdn.com/images/baby_potato/post/cdbd7118-c7ef-482b-a684-f10c5d4ab2eb/image.png" alt=""></p>
<h1 id="nestjs-providers-service-란">NestJS Providers, Service 란?</h1>
<h2 id="provides-란">Provides 란?</h2>
<p>프로바이더는 Nest의 기본 개념이다. 대부분의 기본 Nest 클래스는 서비스, 리포지토리, 팩토리, 헬퍼등 프로바이더로 취급될 수 있다. 프로바이더의 주요 아이디어는 종속성으로 주입할 수 있다는 것이다. 즉, 객체는 서로 다양한 관계를 만들 수 있으며 객체의 인스턴스를 &quot;연결&quot;하는 기능은 대부분 Nest 런타임 시스템에 위임될 수 있다.
=&gt; Controller 에서 필요하는 많은 것들을 Controller 내에서 다 처리할 수 없으므로 기능별로 서비스, 리포지토리, 팩토리, 헬퍼 등을 넣어서 사용한다 .
=&gt; <strong>종속성</strong>을 주입한다.
<img src="https://velog.velcdn.com/images/baby_potato/post/31027d79-1bea-451e-bcbf-c0007b01b868/image.png" alt=""></p>
<h2 id="service-란">Service 란?</h2>
<p><strong>@Injectable</strong> 데코레이터로 감싸져서 모듈에 제공되며, 이 서비스 인스턴스는 애플리케이션 전체에서 사용 될 수 있다.
서비스는 컨트롤러에서 데이터의 유효성 체크를 하거나 데이터베이스에 아이템을 생성하는 등의 작업을 하는 부분을 처리한다.</p>
<h2 id="service를-controller에서-이용할-수-있는-방법-dependency-injection">Service를 Controller에서 이용할 수 있는 방법( Dependency Injection)</h2>
<p><img src="https://velog.velcdn.com/images/baby_potato/post/2b1cbad9-a549-409f-8edc-e13603a74151/image.png" alt="">
위에 보면 Contoller에서 this.appService.getHello(); 이런식으로  Service에 정의해놓은 메소드를 Controller에서 가져와서 쓰는 걸 볼 수 있다. 
<img src="https://velog.velcdn.com/images/baby_potato/post/34b558cb-ef2c-4f27-ae1c-337647511970/image.png" alt="">
위에 보면 BoardsService를 Constructor 클래스에서 가져오고(Injected) 있다. 그런 후에 Private 문법을 사용하고 있다. 이렇게 해서 boardsService를 정의해서 Controller안에서 사용할수 있게 한다. 
=&gt; 이렇게 할 수 있는 이유는 타입스크립트의 기능을 이용해서 종속성을 타입으로 해결할수 있기 때문이다. </p>
<h2 id="provider-등록하기">Provider 등록하기</h2>
<p>Provider를 사용하기 위해서는 이것을 Nest에 등록해줘야지 사용할 수 있다. 
등록하기 위해서는 module 파일에서 할 수 있습다. module 파일에 providers항목안에 해당 모듈에서 사용하고자 하는 Provider 를 넣어주시면 됩니다.
<img src="https://velog.velcdn.com/images/baby_potato/post/9e0c317e-2ce8-48d6-a286-88cf9d9db487/image.png" alt=""></p>
<h1 id="boards-service-만들기">Boards Service 만들기</h1>
<pre><code>nest g service boards --no-spec
=&gt;boards.service.ts 생성</code></pre><h2 id="board-service">Board Service</h2>
<p>boards.service.ts 파일 안에는 Injectable 데코레이터가 있으며 이 NestJS는 이것을 이용해서 다른 컴포넌트에서 이 서비스를 사용 할 수있게(Injectable)만들어준다.
그리고 CLI로 Service 생성시에는 module에도 자동으로 Service가 추가된다.</p>
<pre><code class="language-ts"> providers: [BoardsService]</code></pre>
<h2 id="board-service를-board-controller에서-이용할-수-있게-해주기dependency-injection종속성-주입">Board Service를 Board Controller에서 이용할 수 있게 해주기(Dependency Injection)(종속성 주입)</h2>
<p><img src="https://velog.velcdn.com/images/baby_potato/post/22d98ee7-b602-4f1e-9d94-1d063f98b504/image.png" alt="">
Controller class의 constructor 안에서 종속성 주입이 이루어진다.</p>
<h3 id="원래는">원래는..</h3>
<p><img src="https://velog.velcdn.com/images/baby_potato/post/5ef46138-f867-4bd3-a2ee-424fde8f468c/image.png" alt="">
Typescriptdml 기능으로 코드를 간결하게 할 수 있다.
(자바스크립트에서는 private같은 접근 제한자(Access modifier)를 사용할수 없지만 Typescript에서는 사용가능하다.)
<img src="https://velog.velcdn.com/images/baby_potato/post/cbecd1d4-2c10-4300-a076-3e29f2f80bbe/image.png" alt=""></p>
<ol>
<li>boardsService 파라미터에 BoardsService 객체를 타입으로 지정 </li>
<li>이 boardsService 파라미터를 BoardsController 클래스 안에서 사용하기 위해서 this.boardsService  프로퍼티에  boardsService 파라미터를 할당</li>
<li>하지만 타입스크립트에서는 선언한 값만 객체의 프로퍼티로 사용가능하기 때문에 
위에 <strong>boardsService: BoardsService로 선언</strong> </li>
<li>이렇게 갖게된 boardsService 프로퍼티를 이용해서 BoardsController 클래스안에서 활용을 할수가 있다. <h2 id="접근-제한자를-이용해서-소스-간단하게-하기">접근 제한자를 이용해서 소스 간단하게 하기</h2>
</li>
</ol>
<p><strong>접근 제한자(public, protected, private)</strong> 을 생성자(constructor) 파라미터에 선언
=&gt;접근 제한자가 사용된 생성자 파라미터는 암묵적으로 클래스 프로퍼티로 선언
<img src="https://velog.velcdn.com/images/baby_potato/post/77464913-4814-4889-ba4c-6e522fd5db63/image.png" alt=""></p>
<h2 id="private을-사용하면-">Private을 사용하면 ?</h2>
<p>private이 선언되었기 때문에 boardsService 프로퍼티는 BoardsController 클래스 *<em>내부에서만 *</em> 사용 가능  </p>
<p><img src="https://velog.velcdn.com/images/baby_potato/post/e10a86b0-e249-4c7c-a9e0-f327d1ba0e98/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[2.2.Java - 기본형과 참조형]]></title>
            <link>https://velog.io/@baby_potato/java-%EA%B8%B0%EB%B3%B8%ED%98%95%EA%B3%BC-%EC%B0%B8%EC%A1%B0%ED%98%95</link>
            <guid>https://velog.io/@baby_potato/java-%EA%B8%B0%EB%B3%B8%ED%98%95%EA%B3%BC-%EC%B0%B8%EC%A1%B0%ED%98%95</guid>
            <pubDate>Tue, 09 Apr 2024 06:05:18 GMT</pubDate>
            <description><![CDATA[<h1 id="기본형-vs-참조형1---시작">기본형 vs 참조형1 - 시작</h1>
<p>사용하는 값을 변수에 직접 넣을 수 있는 기
본형, 그리고 이전에 본 Student student1 과 같이 객체가 저장된 메모리의 위치를 가리키는 참조값을 넣을 수 있는 참조형으로 분류할 수 있다.</p>
<ul>
<li>기본형(Primitive Type): int , long , double , boolean 처럼 변수에 사용할 값을 직접 넣을 수 있는 데이터 타입을 기본형이라 한다.<ul>
<li>참조형(Reference Type): Student student1 , int[] students 와 같이 데이터에 접근하기 위한 참조(주소)를 저장하는 데이터 타입을 참조형이라 한다. 참조형은 객체 또는 배열에 사용된다.<ul>
<li>객체는 . (dot)을 통해서 메모리 상에 생성된 객체를 찾아가야 사용할 수 있다.</li>
<li>배열은 [] 를 통해서 메모리 상에 생성된 배열을 찾아가야 사용할 수 있다.</li>
</ul>
</li>
</ul>
</li>
</ul>
<blockquote>
<p>기본형은 연산이 가능하지만 참조형은 연산이 불가능하다.</p>
</blockquote>
<pre><code class="language-java">int a = 10, b = 20;
int sum = a + b;</code></pre>
<pre><code class="language-java">Student s1 = new Student();
Student s2 = new Student();
s1 + s2 //오류 발생</code></pre>
<pre><code class="language-java">Student s1 = new Student();
s1.grade = 100;
Student s2 = new Student();
s2.grade = 90;
int sum = s1.grade + s2.grade; //연산 가능</code></pre>
<blockquote>
<p>기본형을 제외한 나머지는 모두 참조형이다.</p>
</blockquote>
<ul>
<li>기본형은 모두 소문자로 시작한다. int, long, double,boolean...</li>
<li>클래스는 대문자로 시작한다. 클래스는 모두 참조형이다.</li>
</ul>
<h1 id="기본형-vs-참조형2---변수-대입">기본형 vs 참조형2 - 변수 대입</h1>
<p><strong>대원칙: 자바는 항상 변수의 값을 복사해서 대입한다</strong></p>
<ul>
<li>기본형 대입<pre><code class="language-java">int a = 10;
int b = a;</code></pre>
</li>
<li>참조형 대입<pre><code class="language-java">Student s1 = new Student();
Student s2 = s1;</code></pre>
=&gt;_<strong>참조형의 경우 실제 사용하는 객체가 아니라 객체의 위치를 가리키는 참조값만 복사</strong>_된다. 쉽게 이야기해서 실제 건물이 복사가 되는 것이 아니라 건물의 위치인 주소만 복사되는 것이다. 따라서 같은 건물을 찾아갈 수 있는 방법이 하나 늘어날 뿐이다.</li>
</ul>
<h2 id="기본형과-변수-대입">기본형과 변수 대입</h2>
<p>&lt;VarChange1.java&gt;</p>
<pre><code class="language-java">public class VarChange1 {
    public static void main(String[] args) {
        int a = 10;
        int b = a;
        System.out.println(&quot;a = &quot; + a); //10
        System.out.println(&quot;b = &quot; + b); //10

        //a 변경
        a = 20;
        System.out.println(&quot;변경 a = 20&quot;);
        System.out.println(&quot;a = &quot; + a); //20
        System.out.println(&quot;b = &quot; + b); //10

        //b 변경
        b = 30;
        System.out.println(&quot;변경 b = 30&quot;);
        System.out.println(&quot;a = &quot; + a); //20
        System.out.println(&quot;b = &quot; + b); //30
    }
}</code></pre>
<h2 id="참조형-변수-대입">참조형 변수 대입</h2>
<p>&lt;Data.java&gt;</p>
<pre><code class="language-java">package ref;

public class Data {
    int value;
}</code></pre>
<p>&lt;VarChange2.java&gt;</p>
<pre><code class="language-java">package ref;

public class VarChange2 {

        public static void main(String[] args) {
            Data dataA = new Data();
            dataA.value = 10;
            Data dataB = dataA;

            System.out.println(&quot;dataA 참조값=&quot; + dataA);
            System.out.println(&quot;dataB 참조값=&quot; + dataB);
            System.out.println(&quot;dataA.value = &quot; + dataA.value); //10
            System.out.println(&quot;dataB.value = &quot; + dataB.value); //10

            //dataA 변경
            dataA.value = 20;
            System.out.println(&quot;변경 dataA.value = 20&quot;);
            System.out.println(&quot;dataA.value = &quot; + dataA.value); //20
            System.out.println(&quot;dataB.value = &quot; + dataB.value); //20

            //dataB 변경
            dataB.value = 30;
            System.out.println(&quot;변경 dataB.value = 30&quot;);
            System.out.println(&quot;dataA.value = &quot; + dataA.value); //30
            System.out.println(&quot;dataB.value = &quot; + dataB.value); //30
        }

}</code></pre>
<p><img src="https://velog.velcdn.com/images/baby_potato/post/d73bb465-d945-4843-b3fc-7a0a62916397/image.png" alt="">
** 참조값이 같다.** 
<img src="https://velog.velcdn.com/images/baby_potato/post/6701904a-7e51-44e1-904e-8ef1b3aab4d6/image.png" alt="">
dataA와 dataB는 같은 참조값을 가지게 되고, 두 변수는 같은 객체 인스턴스를 참조하게된다.</p>
<h1 id="기본형-vs-참조형3---메서드-호출">기본형 vs 참조형3 - 메서드 호출</h1>
<h2 id="기본형과-메서드-호출">기본형과 메서드 호출</h2>
<p>&lt;MethodChange1.java&gt;</p>
<pre><code class="language-java">package ref;

public class MethodChange1 {
    public static void main(String[] args) {
        int a = 10;
        System.out.println(&quot;메서드 호출 전: a = &quot; + a); //10
        changePrimitive(a);
        System.out.println(&quot;메서드 호출 후: a = &quot; + a); //10
    }
    static void changePrimitive(int x) {
        x = 20;
    }
}</code></pre>
<p>1.<img src="https://velog.velcdn.com/images/baby_potato/post/88c910c1-4c78-454d-9685-d020889040d6/image.png" alt="">
a , x 각각 숫자 10 을 가지고 있다.</p>
<p>2.<img src="https://velog.velcdn.com/images/baby_potato/post/2fe1385b-881a-4a0c-b2da-36c855521952/image.png" alt="">
x 의 값만 20 으로 변경되고, a 의 값은 10 으로 유지</p>
<h2 id="참조형과-메서드-호출">참조형과 메서드 호출</h2>
<p>&lt;MethodChange2.java&gt;</p>
<pre><code class="language-java">package ref;

public class MethodChange2 {
    public static void main(String[] args) {
        Data dataA = new Data();
        dataA.value = 10;
        System.out.println(&quot;메서드 호출 전: dataA.value = &quot; + dataA.value); // 10
        changeReference(dataA);
        System.out.println(&quot;메서드 호출 후: dataA.value = &quot; + dataA.value);// 20
    }
    static void changeReference(Data dataX) {
        dataX.value = 20;
    }
}</code></pre>
<p><img src="https://velog.velcdn.com/images/baby_potato/post/e97c4229-a617-4904-bdfa-da2c5c9d9594/image.png" alt="">
dataX 에 변수 dataA 의 값을 전달</p>
<pre><code class="language-java">Data dataX = dataA</code></pre>
<p>변수 dataA 는 참조값 x001 을 가지고 있으므로 참조값을 복사해서 전달했다. 따라서 변수 dataA , dataX 둘다 같은 참조값인 x001 을 가지게 된다.
이제 dataX 를 통해서도 x001 에 있는 Data 인스턴스에 접근할 수 있다.</p>
<h3 id="기본형과-참조형의-메서드-호출">기본형과 참조형의 메서드 호출</h3>
<ul>
<li>기본형: 메서드로 기본형 데이터를 전달하면, 해당 값이 복사되어 전달된다. 이 경우, 메서드 내부에서 매개변수(파라미터)의 값을 변경해도, <strong>호출자의 변수 값에는 영향이 없다</strong>.</li>
<li>참조형: 메서드로 참조형 데이터를 전달하면, 참조값이 복사되어 전달된다. 이 경우, <strong>메서드 내부에서 매개변수(파라미터)로 전달된 객체의 멤버 변수를 변경하면, 호출자의 객체도 변경</strong>된다.</li>
</ul>
<h2 id="참조형과-메서드-호출---활용">참조형과 메서드 호출 - 활용</h2>
<p>&lt;Method1.java&gt;</p>
<pre><code class="language-java">package ref;

public class Method1 {
    public static void main(String[] args) {
        Student student1 = new Student();
        initStudent(student1,&quot;학생1&quot;, 16, 85);

        Student student2 = new Student();
        initStudent(student2, &quot;학생2&quot;,19, 90);

        printStudent(student1);
        printStudent(student2);
    }
    static void initStudent(Student student, String name, int age, int grade){
        student.name = name;
        student.age = age;
        student.grade = grade;
    }
    static void printStudent(Student student){
        System.out.println(&quot;이름:&quot; + student.name + &quot; 나이:&quot; + student.age + &quot; 성적:&quot; + student.grade);
    }

}</code></pre>
<p><img src="https://velog.velcdn.com/images/baby_potato/post/b53ff60a-cb69-409a-8a6e-1956f5691c1b/image.png" alt="">
** 주의 **</p>
<pre><code class="language-java">```java
package ref;

import class1.Student;

public class Method1 {
 ...
}</code></pre>
<ul>
<li>import class1.Student; 이 선언되어 있으면 안된다.</li>
</ul>
<h2 id="메서드에서-객체-변환">메서드에서 객체 변환</h2>
<p>학생정보를 입력하는 부분이 중복으로 나오는데 이부분도 하나로 합쳐보자.
&lt;Method2.java&gt;</p>
<pre><code class="language-java">package ref;

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

        Student student1 = createStudent(&quot;학생1&quot;, 16, 85);
        Student student2 = createStudent( &quot;학생2&quot;,19, 90);

        printStudent(student1);
        printStudent(student2);
    }
    static Student createStudent(String name, int age, int grade){
        Student student = new Student();
        student.name = name;
        student.age = age;
        student.grade = grade;
        return student;
    }
    static void printStudent(Student student){
        System.out.println(&quot;이름:&quot; + student.name + &quot; 나이:&quot; + student.age + &quot; 성적:&quot; + student.grade);
    }

}</code></pre>
<p>createStudent() 는 생성한 Student 인스턴스의 참조값을 반환한다. 이렇게 반환된 참조값을 student1 변수에 저장했다. 앞으로는 student1 을 통해 Student 인스턴스를 사용할 수 있다. </p>
<h1 id="변수와-초기화">변수와 초기화</h1>
<h2 id="변수의-종류">변수의 종류</h2>
<ul>
<li>멤버변수(필드) : 클래스에 선언</li>
<li>지역변수 : 메서드에 선언, 매개변수도 지역변수의 한 종류이다.<h2 id="변수의-값-초기화">변수의 값 초기화</h2>
</li>
<li>멤버변수 : 자동초기화<ul>
<li>인스턴스의 멤버 변수는 인스턴스를 생성할때 자동으로 초기화된다.</li>
<li>int =0, boolean = false, 참조형 =null</li>
</ul>
</li>
<li>지역변수 : 수동 초기화</li>
</ul>
<h1 id="null">null</h1>
<h2 id="null-값-할당">null 값 할당</h2>
<p>&lt;NullMain1.java&gt;</p>
<pre><code class="language-java">package ref;

public class NullMain1 {
    public static void main(String[] args) {
        Data data = null;
        System.out.println(&quot;1.  data = &quot;+data);
        data = new Data();
        System.out.println(&quot;2.  data = &quot;+data);
        data=null;
        // data 변수는 앞서 만든 Data 인스턴스를 더는 참조하지 않는다.
        System.out.println(&quot;3.  data = &quot;+data);
    }
}</code></pre>
<h3 id="gc가비지-컬렉션">GC(가비지 컬렉션)</h3>
<p>data 에 null 을 할당하면 생성한 x001 Data 인스턴스를 더는 아무도 참조하지 않는다. 이렇게 아무도 참조하지 않게 되면 x001 이라는 참조값을 다시 구할 방법이 없다. 따라서 해당 인스턴스에 다시 접근할 방법이 없다.
-&gt; 아무도 참조하지 않는 인스턴스는 메모리 낭비
-&gt; 자바는 낭비되는 메모리를 자동으로 삭제해준다. </p>
<h1 id="nullpointerexception">NullPointerException</h1>
<p>null 을 가리키다(Pointer)인데, 이때 발생하는 예외(Exception)다. 
null 은 없다는 뜻이므로 결국 주소가 없는 곳을 찾아갈 때 발생하는 예외이다.</p>
<p>객체를 참조할 때는 . (dot)을 사용한다. 이렇게 하면 참조값을 사용해서 해당 객체를 찾아갈 수 있다. 그런데 참조값이 null 이라면 값이 없다는 뜻이므로, 찾아갈 수 있는 객체(인스턴스)가 없다. NullPointerException 은 이처럼 <strong>null 에 . (dot)을 찍었을 때 발생</strong>한다.</p>
<p>&lt;NullMain2.java&gt;</p>
<pre><code class="language-java">package ref;

public class NullMain2 {
    public static void main(String[] args) {
        Data data = null;
        data.value = 10; //=null.value = 10
        System.out.println(&quot;data = &quot;+data.value);
    }
}</code></pre>
<p>=&gt; java.lang.NullPointerException 이 발생</p>
<h2 id="멤버변수와-null">멤버변수와 null</h2>
<p>&lt;NullMain3.java&gt;</p>
<pre><code class="language-java">package ref;

public class NullMain3 {
    public static void main(String[] args) {
        BigData bigData = new BigData();
        System.out.println(&quot;bigData.count = &quot;+ bigData.count);
        System.out.println(&quot;bigData.data = &quot;+ bigData.data);

        //NullPointerException
        System.out.println(&quot;bigData.data.value=&quot; + bigData.data.value);
        //null 값에 .(dot)을 찍으면 예외가 발생한다.
    }
}</code></pre>
<p>=&gt; null 에러</p>
<p>&lt;NullMain4.java&gt;</p>
<pre><code class="language-java">package ref;

public class NullMain4 {
    public static void main(String[] args) {
        BigData bigData = new BigData();
        bigData.data = new Data();
        System.out.println(&quot;bigData.count = &quot;+ bigData.count);
        System.out.println(&quot;bigData.data = &quot;+ bigData.data);

        System.out.println(&quot;bigData.data.value=&quot; + bigData.data.value);
    }
}</code></pre>
<h1 id="문제와-풀이">문제와 풀이</h1>
<p>&lt;ProductOrderMain2.java&gt;</p>
<pre><code class="language-java">package ref.ex;

public class ProductOrderMain2 {
    public static void main(String[] args) {
        int total;
        ProductOrder[] productOrders = {
                createOrder(&quot;사과&quot;,2000,3),
                createOrder(&quot;바나나&quot;,1000,7)
        };
        printOrder(productOrders);
        total= getTotal(productOrders);
        System.out.println(&quot;총 금액 : &quot;+total);

    }
    static ProductOrder createOrder(String name, int price, int quantity){
        ProductOrder productOrder = new ProductOrder();
        productOrder.name = name;
        productOrder.price = price;
        productOrder.quantity = quantity;

        return productOrder;
    }
    static void printOrder(ProductOrder[] orders){

        for (ProductOrder order :orders){
            System.out.println(&quot;이름 : &quot;+order.name +&quot;  가격 : &quot;+ order.price+ &quot;   수량 : &quot;+order.quantity);
        }
    }

    public static int  getTotal(ProductOrder[] orders){
        int total =0;
        for(ProductOrder order: orders){
            total += order.price*order.quantity;
        }
        return total;
    }
}</code></pre>
<p>&lt;ProductOrderMain3.java&gt;</p>
<pre><code class="language-java">package ref.ex;

import java.util.Scanner;

public class ProductOrderMain3 {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        System.out.print(&quot;입력할 주문의 개수를 입력하세요:&quot;);
        int count = input.nextInt();
        input.nextLine();

        ProductOrder[] productOrders = new ProductOrder[count];

        for(int i =0; i&lt;count;i++){
            System.out.println((i+1)+&quot;번째 주문 정보를 입력하세요.&quot;);

            System.out.print(&quot;상품명: &quot; );
            String name = input.nextLine();

            System.out.print(&quot;가격: &quot;);
            int price = input.nextInt();

            System.out.print(&quot;수량: &quot;);
            int quantity = input.nextInt();

            input.nextLine(); // 입력 버퍼를 비우기 위한 코드

            productOrders[i]=createOrder(name, price, quantity);
        }

        printOrder(productOrders);
        int total= getTotal(productOrders);
        System.out.println(&quot;총 금액 : &quot;+total);

    }
    static ProductOrder createOrder(String name, int price, int quantity){
        ProductOrder productOrder = new ProductOrder();
        productOrder.name = name;
        productOrder.price = price;
        productOrder.quantity = quantity;

        return productOrder;
    }
    static void printOrder(ProductOrder[] orders){

        for (ProductOrder order :orders){
            System.out.println(&quot;이름 : &quot;+order.name +&quot;  가격 : &quot;+ order.price+ &quot;   수량 : &quot;+order.quantity);
        }
    }

    public static int  getTotal(ProductOrder[] orders){
        int total =0;
        for(ProductOrder order: orders){
            total += order.price*order.quantity;
        }
        return total;
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[2.1.Java - 클래스와 데이터]]></title>
            <link>https://velog.io/@baby_potato/%EC%9E%90%EB%B0%94-%ED%81%B4%EB%9E%98%EC%8A%A4%EC%99%80-%EB%8D%B0%EC%9D%B4%ED%84%B0</link>
            <guid>https://velog.io/@baby_potato/%EC%9E%90%EB%B0%94-%ED%81%B4%EB%9E%98%EC%8A%A4%EC%99%80-%EB%8D%B0%EC%9D%B4%ED%84%B0</guid>
            <pubDate>Tue, 09 Apr 2024 02:24:23 GMT</pubDate>
            <description><![CDATA[<h1 id="프로젝트-환경-구성">프로젝트 환경 구성</h1>
<p><img src="https://velog.velcdn.com/images/baby_potato/post/fbee329c-63c1-44cd-9855-e8a94f203548/image.png" alt=""></p>
<h1 id="클래스가-필요한-이유">클래스가 필요한 이유</h1>
<p>&lt;ClassStart1.java&gt;</p>
<pre><code class="language-java">package class1;

public class ClassStart1 {
    public static void main(String[] args) {
        String student1Name = &quot;학생1&quot;;
        int student1Age = 15;
        int student1Grade = 90;

        String student2Name = &quot;학생2&quot;;
        int student2Age = 16;
        int student2Grade = 80;

        System.out.println(&quot;이름:&quot; + student1Name + &quot; 나이:&quot; + student1Age + &quot; 성 적:&quot; + student1Grade);
        System.out.println(&quot;이름:&quot; + student2Name + &quot; 나이:&quot; + student2Age + &quot; 성 적:&quot; + student2Grade);
    }
}</code></pre>
<p>=&gt; 학생이 늘어날 때 마다 변수를 추가로 선언해야하고, 출력코드 또한 추가해야한다.</p>
<p>&lt;ClassStart2.java&gt;</p>
<pre><code class="language-java">package class1;

public class ClassStart2 {
    public static void main(String[] args) {
        String[] studentNames = {&quot;학생1&quot;, &quot;학생2&quot;};
        int[] studentAges = {15, 16};
        int[] studentGrades = {90, 80};

        for (int i = 0; i &lt; studentNames.length; i++) {
            System.out.println(&quot;이름:&quot; + studentNames[i] + &quot; 나이:&quot; +
                    studentAges[i] + &quot; 성적:&quot; + studentGrades[i]);
        }
    }
}</code></pre>
<p>=&gt; 배열을 활용, 배열에 학생의 데이터만 추가하면 된다.
=&gt; 배열의 한계 : 한 학생의 데이터를 지우기 위해 3개의 배열의 인덱스를 맞춰줘야한다.</p>
<blockquote>
<p>학생이라는 개념을 하나로 묶고, 각각의 학생 별로 본인의 이름, 나이, 성적을 관리하자!</p>
</blockquote>
<h1 id="클래스-도입">클래스 도입</h1>
<p>&lt;Student 클래스&gt;
&lt;Student.java&gt;</p>
<pre><code class="language-java">package class1;

public class Student {
    String name;
    int age;
    int grade;
}</code></pre>
<p>=&gt; class 키워드를 사용해서 학생클래스(Student)를 정의, 학생 클래스 내부에 이름, 나이, 성적 변수를 가진다.</p>
<p>이렇게** 클래스에 정의한 변수들을 멤버변수 또는 필드**라고 한다.</p>
<blockquote>
<ul>
<li>멤버변수(Member Variable): 특정 클래스에 소속된 멤버</li>
</ul>
</blockquote>
<ul>
<li>필드(Field): 데이터 항목을 가리키는 용어</li>
<li>자바에서 멤버변수, 필드는 같은 뜻이고 클래스에 소속된 변수를 뜻한다.</li>
</ul>
<blockquote>
<p>클래스는 관례상 대문자로 시작하고 낙타표기법을 사용</p>
</blockquote>
<p>&lt;ClassStart3.java&gt;</p>
<pre><code class="language-java">package class1;

public class ClassStart3 {
    public static void main(String[] args) {
        Student student1;
        student1 = new Student();
        student1.name = &quot;학생1&quot;;
        student1.age = 15;
        student1.grade = 90;

        Student student2 = new Student();
        student2.name = &quot;학생2&quot;;
        student2.age = 16;
        student2.grade = 80;

        System.out.println(&quot;이름:&quot; + student1.name + &quot; 나이:&quot; + student1.age
                + &quot; 성 적:&quot; + student1.grade);
        System.out.println(&quot;이름:&quot; + student2.name + &quot; 나이:&quot; + student2.age
                + &quot; 성 적:&quot; + student2.grade);
    }
}</code></pre>
<h2 id="클래스와-사용자-정의-타입">클래스와 사용자 정의 타입</h2>
<ul>
<li>클래스를 사용하면 int, String 과 같은 타입을 만들 수 있다.</li>
<li>사용자가 직접 정의하는 사용자의 정의타입을 만들려변 설계도가 필요하다. 이 <strong>설계도가 바로 클래스</strong>이다.</li>
<li>설계도인 클래스를 사용해 <strong>실제 메모리에 만들어진 실체를 객체 또는 인스턴스</strong>라고 한다.</li>
<li>클래스를 통해 사용자가 원하는 종류의 타입을 마음껏 정의할 수 있다.</li>
</ul>
<p><em><strong>Student 클래스를 기반으로 student1, student2 객체 또는 인스턴스를 만들었다.</strong></em></p>
<h1 id="객체사용">객체사용</h1>
<p>클래스를 통해 생성한 객체를 사용하려면 먼저 메모리에 존재하는 객체에 접근해야 한다. 객체에 접근하려면 . (점, dot)을 사용하면 된다.</p>
<p>student1.name
student1.age
student2.grade
...</p>
<h1 id="클래스-객체-인스턴스">클래스, 객체, 인스턴스</h1>
<h2 id="클래스-class">클래스-Class</h2>
<p>클래스는 객체가 가져야할 속석(변수)과 기능(메서드)를 정의한다. 예를 들어 학생이라는 클래스 속성으로 name, age, grade를 가진다. </p>
<h2 id="객체---object">객체 - Object</h2>
<p>객체는 클래스에서 정의한 속성과 기능을 가진 실체이다. 객체는 서로 독립적인 상태를 가진다.
student1은 학생1의 속성을 가지는 객체이다.</p>
<h2 id="인스턴스---instance">인스턴스 - Instance</h2>
<p>인스턴스는 특정 믈래스로부터 생성된 객체를 의미한다. 인스턴스는 주로 객체가 어떤 클래스에 속해 있는지 강조할 때 사용한다. 
student1 객체는 Student 클래스의 인스턴스이다.</p>
<h2 id="객체-vs-인스턴스">객체 vs 인스턴스</h2>
<p>인스턴스는 객테보다 좀 더 관계에 초점을 맞춘 단어이다. 보통 student1 은 Student 의 객체이다. 라고 말하는 대신 student1 은 Student 의 인스턴스이다. 라고 특정 클래스와의 관계를 명확히 할 때 인스턴스라는 용어를 주로 사용한다.
하지만 둘다 클래스에서 나온 실체라는 핵심 의미는 같기 때문에 보통 둘을 구분하지 않고 사용한다.</p>
<h1 id="배열-도입---시작">배열 도입 - 시작</h1>
<p>배열을 사용하면 특정 타입을 연속한 데이터 구조로 묶어서 편리하게 관리할 수 있다.
&lt;ClassStart4.java&gt;</p>
<pre><code class="language-java">package class1;

public class ClassStart4 {
    public static void main(String[] args) {
        Student student1 = new Student();
        student1.name = &quot;학생1&quot;;
        student1.age = 15;
        student1.grade = 90;

        Student student2 = new Student();
        student2.name = &quot;학생2&quot;;
        student2.age = 16;
        student2.grade = 80;

        Student[] students = new Student[2];
        students[0]= student1;
        students[1]= student2;

        System.out.println(&quot;이름:&quot; + students[0].name + &quot; 나이:&quot; + students[0].age
                + &quot; 성적:&quot; + students[0].grade);
        System.out.println(&quot;이름:&quot; + students[1].name + &quot; 나이:&quot; + students[1].age
                + &quot; 성적:&quot; + students[1].grade);
    }
}</code></pre>
<h1 id="배열-도입---리펙토링">배열 도입 - 리펙토링</h1>
<pre><code class="language-java">package class1;

public class ClassStart5 {
    public static void main(String[] args) {
        Student student1 = new Student();
        student1.name = &quot;학생1&quot;;
        student1.age = 15;
        student1.grade = 90;

        Student student2 = new Student();
        student2.name = &quot;학생2&quot;;
        student2.age = 16;
        student2.grade = 80;

        //배열 선언
        Student[] students = new Student[]{student1, student2};

        //for문 적용
        for (int i = 0; i &lt; students.length; i++) {
            System.out.println(&quot;이름:&quot; + students[i].name + &quot; 나이:&quot; +
                    students[i].age + &quot; 성적:&quot; + students[i].grade);
        }
    }
}</code></pre>
<p>Student[] students = new Student[]{student1, student2};
-&gt; Student[] students = {student1, student2}; 
최적화 가능</p>
<pre><code class="language-java">for (int i = 0; i &lt; students.length; i++) {
            System.out.println(&quot;이름:&quot; + students[i].name + &quot; 나이:&quot; +
                    students[i].age + &quot; 성적:&quot; + students[i].grade);
        }</code></pre>
<p>=&gt;</p>
<pre><code class="language-java">for (Student s : students) {
 System.out.println(&quot;이름:&quot; + s.name + &quot; 나이:&quot; + s.age + &quot; 성적:&quot; + s.grade);
}</code></pre>
<h1 id="문제와-풀이">문제와 풀이</h1>
<p>&lt;MovieReview.java&gt;</p>
<pre><code class="language-java">package class1.ex;

public class MovieReview {
    String title;
    String review;

}</code></pre>
<p>&lt;MovieReviewMain1.java&gt;</p>
<pre><code class="language-java">package class1.ex;

public class MovieReviewMain1 {
    public static void main(String[] args) {
        MovieReview movieReview = new MovieReview();

        movieReview.title=&quot;쿵푸팬더&quot;;
        movieReview.review=&quot;재밌다.&quot;;

        System.out.println(&quot;영화 제목 : &quot;+movieReview.title+&quot; 리뷰 : &quot;+movieReview.review);
    }
}</code></pre>
<p>&lt;MovieReviewMain2.java&gt;</p>
<pre><code class="language-java">package class1.ex;

public class MovieReviewMain1 {
    public static void main(String[] args) {
        MovieReview[] reviews = new MovieReview[2];

        MovieReview inception = new MovieReview();
        inception.title = &quot;인셉션&quot;;
        inception.review = &quot;인생은 무한 루프&quot;;
        reviews[0] = inception;

        MovieReview aboutTime = new MovieReview();
        aboutTime.title = &quot;어바웃 타임&quot;;
        aboutTime.review = &quot;인생 시간 영화!&quot;;
        reviews[1] = aboutTime;

        for (MovieReview review : reviews) {
            System.out.println(&quot;영화 제목: &quot; + review.title + &quot;, 리뷰: &quot; +
                    review.review);
        }
    }
}</code></pre>
<h1 id="문제와-풀이2">문제와 풀이2</h1>
<p>&lt;ProductOrder.java&gt;</p>
<pre><code class="language-java">package class1.ex;

public class ProductOrder {
        String name;
        int price;
        int quantity;

}
</code></pre>
<p>&lt;ProductOrderMain1.java&gt;</p>
<pre><code class="language-java">package class1.ex;

public class ProductOrderMain1 {
    public static void main(String[] args) {
        int total =0;

        ProductOrder order1 = new ProductOrder();
        order1.name =  &quot;사과&quot;;
        order1.price=2000;
        order1.quantity=3;

        ProductOrder order2 = new ProductOrder();
        order2.name =  &quot;바나나&quot;;
        order2.price=1000;
        order2.quantity=7;

        ProductOrder[] orders = {order1,order2};

        for(ProductOrder o : orders){
            System.out.println(&quot;상품명:&quot; +o.name+ &quot;  가격:&quot; +o.price+ &quot;  수량:&quot; +o.quantity);
            total += o.price*o.quantity;
        }
        System.out.println(&quot;총 금액: &quot;+ total);

    }
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[JAVA 멍충이 탈출기 마지막]]></title>
            <link>https://velog.io/@baby_potato/JAVA-%EB%A9%8D%EC%B6%A9%EC%9D%B4-%ED%83%88%EC%B6%9C%EA%B8%B0-%EB%A7%88%EC%A7%80%EB%A7%89</link>
            <guid>https://velog.io/@baby_potato/JAVA-%EB%A9%8D%EC%B6%A9%EC%9D%B4-%ED%83%88%EC%B6%9C%EA%B8%B0-%EB%A7%88%EC%A7%80%EB%A7%89</guid>
            <pubDate>Mon, 08 Apr 2024 10:05:58 GMT</pubDate>
            <description><![CDATA[<h1 id="메서드">메서드</h1>
<h2 id="메서드-시작">메서드 시작</h2>
<h3 id="함수-function">함수 (function)</h3>
<p>add(a, b) = a + b</p>
<ul>
<li>이름이 add 이고 a , b 라는 두 값을 받는 함수이다. 그리고 이 함수는 a + b 연산을 수행한다.</li>
<li>add(1,2) -&gt; 결과:3
add(5,6) -&gt; 결과:11
add(3,5) -&gt; 결과:8</li>
<li>같은 함수를 다른 입력 값으로 여러번 호출 할 수 있다.</li>
<li><strong>재사용이 가능!</strong></li>
</ul>
<h2 id="메서드-사용">메서드 사용</h2>
<p>&lt;Method1Ref.java&gt;</p>
<pre><code class="language-java">package method;

public class Method1Ref {
    public static void main(String[] args) {
        int sum1 = add(5, 10);
        System.out.println(&quot;결과1 출력:&quot; + sum1);

        int sum2 = add(15, 20);
        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>
<h3 id="메서드-선언">메서드 선언</h3>
<p><strong>public static int add(int a, int b)</strong></p>
<ul>
<li>메서드 이름, 반환타입, 매개변수(파라미터)</li>
<li>public : 다른 클래스에서 호출할 수 있는 메서드라는 뜻
static :  객체를 생성하지 않고 호출할 수 있는 정적 메서드라는 뜻
int : 반환 타입을 정의, 메서드 실행결과를 반환할 때 사용할 반환 타입을 지정한다.
add : 메서드 이름
(int a, int b) : 메서드를 호출할 때 전달하는 입력값을 정의, 파라미터, 매개변수</li>
</ul>
<h3 id="메서드-본문">메서드 본문</h3>
<pre><code class="language-java">//1: 메서드 호출
int sum1 = add(5, 10);
//2: 파라미터 변수 a=5, b=10이 전달되면서 메서드가 수행된다.
public static int add(int a=5, int b=10) {
 int sum = a + b;
 return sum;
}
//3: 메서드가 수행된다.
public static int add(int a=5, int b=10) {
 int sum = a(5) + b(10);
 return sum;
}
//4: return을 사용해서 메서드 실행의 결과인 sum을 반환한다. sum에는 값 15가 들어있으므로 값 15가
반환된다.
public static int add(int a=5, int b=10) {
 int sum = 15;
 return sum(15);
}
//5: 메서드 호출 결과로 메서드에서 반환한 값 15가 나온다. 이 값을 sum1에 대입했다.
int sum1 = 15</code></pre>
<h3 id="메서드-호출과-용어정리">메서드 호출과 용어정리</h3>
<p>메서드를 호출할 때는 다음과 같이 메서드에 넘기는 값과 매개변수(파라미터)의 타입이 맞아야 한다. 물론 넘기는 값과
매개변수(파라미터)의 순서와 갯수도 맞아야 한다.</p>
<pre><code>호출: call(&quot;hello&quot;, 20)
메서드 정의: int call(String str, int age)</code></pre><ul>
<li>인수(Argument) : &#39;hello&#39;, 20처럼 넘기는 값을 Argument, 인자, 인수라고 한다.</li>
<li>매개변수(Parameter) : 메서드를 정의할 때 선언한 변수인 String str , int age 를 매개변수, 파라미터라 한다.
메서드를 호출할 때 인수를 넘기면, 그 인수가 매개변수에 대입된다.<blockquote>
<ul>
<li>인수라는 용어는 &#39;인’과 &#39;수’의 합성어로, &#39;들어가는 수’라는 의미를 가진다. 즉, 메서드 내부로 들어가는 값을 의미한다. 인자도 같은 의미이다.</li>
<li>매개변수, parameter는 &#39;매개’와 &#39;변수’의 합성어로, &#39;중간에서 전달하는 변수’라는 의미를 가진다. 즉, 메서드 호출부와 메서드 내부 사이에서 값을 전달하는 역할을 하는 변수라는 뜻이다.</li>
</ul>
</blockquote>
</li>
</ul>
<h2 id="메서드-정의">메서드 정의</h2>
<pre><code class="language-java">public static int add(int a, int b) {
 //메서드 본문, 실행 코드
}
제어자 반환타입 메서드이름(매개변수 목록) {
 메서드 본문
}</code></pre>
<ul>
<li>제어자(Modifier): public , static 과 같은 부분이다. 제어자는 뒤에서 설명한다. 지금은 항상 public static 키워드를 입력하자.</li>
<li>반환 타입(Return Type): 메서드가 실행 된 후 반환하는 데이터의 타입을 지정한다. 메서드가 값을 반환하지 않는 경우, 없다는 뜻의 void 를 사용해야 한다. 예) void print(String str)</li>
<li>메서드 이름(Method Name): 메서드의 이름이다. 이 이름은 메서드를 호출하는 데 사용된다.</li>
<li>매개변수(Parameter): 입력 값으로, 메서드 내부에서 사용할 수 있는 변수이다. 매개변수는 옵션이다. 입력값이필요 없는 메서드는 매개변수를 지정하지 않아도 된다. 예) add()</li>
<li>메서드 본문(Method Body): 실제 메서드의 코드가 위치한다. 중괄호 {} 사이에 코드를 작성한다.</li>
</ul>
<h3 id="매개변수나-반환타입이-없는-경우">매개변수나 반환타입이 없는 경우</h3>
<p>&lt;Method2.java&gt;</p>
<pre><code class="language-java">package method;

public class Method2 {
    public static void main(String[] args) {
        printHeader();
        System.out.println(&quot;프로그램이 동작합니다.&quot;);
        printFooter();
    }
    public static void printHeader() {
        System.out.println(&quot;= 프로그램을 시작합니다 =&quot;);
        return; //void의 경우 생략 가능
    }
    public static void printFooter() {
        System.out.println(&quot;= 프로그램을 종료합니다 =&quot;);
    }
}</code></pre>
<p>=&gt; String str = printHeader(); 반환 타입이 void 이기 때문에 이렇게 반환 값을 받으면 컴파일 오류가 발생한다.</p>
<p><strong>void 와 return 생략</strong>
반환 타입 void 의 경우에는 예외로 printFooter() 와 같이 생략해도 된다. 자바가 반환 타입이 없는 경우에는 return 을 마지막줄에 넣어준다. 참고로 return 을 만나면 해당 메서드는 종료된다.</p>
<h2 id="반환타입">반환타입</h2>
<p>&lt;MethodReturn1.java&gt;</p>
<pre><code class="language-java">package method;

public class MethodReturn1 {
    public static void main(String[] args) {
        boolean result = odd(2);
        System.out.println(result);
    }
    public static boolean odd(int i){
        if(i%2==0){
            return true;
        }else {
            return false;
        }
    }
}</code></pre>
<blockquote>
<p>return 문을 만나면 그 즉시 메서드를 빠져나간다.</p>
</blockquote>
<p>&lt;MethodReturn2.java&gt;</p>
<pre><code class="language-java">package method;

public class MethodReturn2 {
    public static void main(String[] args) {
        checkAge(10);
        checkAge(20);
    }
    public static void checkAge(int age) {
        if (age &lt; 18) {
            System.out.println(age + &quot;살, 미성년자는 출입이 불가능합니다.&quot;);
            return;
        }
        System.out.println(age + &quot;살, 입장하세요.&quot;);
    }
}</code></pre>
<h2 id="메서드-호출과-값-전달1">메서드 호출과 값 전달1</h2>
<p>&lt;MethodValue1.java&gt;</p>
<pre><code class="language-java">package method;

public class MethodValue1 {
    public static void main(String[] args) {
        int num1 =5;
        System.out.println(&quot;1. changeNum 호출 전, num1 : &quot;+num1);
        changeNum(num1);
        System.out.println(&quot;4. changeNum 호출 후, num1 : &quot;+num1);
    }
    public static void changeNum(int num2){
        System.out.println(&quot;2. changeNym 변경 전, num2 : &quot;+num2);
        num2 *= 2;
        System.out.println(&quot;3. changeNym 변경 후, num2 : &quot;+num2);
    }
}</code></pre>
<h2 id="메서드-호출과-값-전달2">메서드 호출과 값 전달2</h2>
<h3 id="메서드-호출과-이름이-같은-변수">메서드 호출과 이름이 같은 변수</h3>
<p>&lt;MethodValue2.java&gt;</p>
<pre><code class="language-java">package method;

public class MethodValue2 {
    public static void main(String[] args) {
        int num =5;
        System.out.println(&quot;1. changeNum 호출 전, num1 : &quot;+num);
        changeNum(num);
        System.out.println(&quot;4. changeNum 호출 후, num1 : &quot;+num);
    }
    public static void changeNum(int num){
        System.out.println(&quot;2. changeNym 변경 전, num2 : &quot;+num);
        num *= 2;
        System.out.println(&quot;3. changeNym 변경 후, num2 : &quot;+num);
    }
}</code></pre>
<p>main() 에 정의한 변수와 메서드의 매개변수(파라미터)의 이름이 둘다 number 로 같다.
하지만 main() 의 number 와 changeNumber() 의 number 는 서로 다른 변수이다.</p>
<h3 id="메서드-호출과-값-반환받기">메서드 호출과 값 반환받기</h3>
<p>메서드를 사용해서 값을 변경하기
&lt;MethodValue3.java&gt;</p>
<pre><code class="language-java">package method;

public class MethodValue3 {
    public static void main(String[] args) {
        int num =5;
        System.out.println(&quot;1. changeNum 호출 전, num1 : &quot;+num);
        num = changeNum(num);
        System.out.println(&quot;2. changeNum 호출 후, num1 : &quot;+num);
    }
    public static int changeNum(int num){
        num *= 2;
        return num;
    }
}</code></pre>
<h2 id="메서드와-형변환">메서드와 형변환</h2>
<h3 id="명시적-형변환">명시적 형변환</h3>
<p>&lt;MethodCasting1.java&gt;</p>
<pre><code class="language-java">package method;

public class MethodCasting1 {
    public static void main(String[] args) {
        double number = 1.5;
        //printNumber(number); // double을 int형에 대입하므로 컴파일 오류
        printNumber((int) number); // 명시적 형변환을 사용해 double을 int로 변환
    }
    public static void printNumber(int n) {
        System.out.println(&quot;숫자: &quot; + n);
    }
}</code></pre>
<h3 id="자동-형변환">자동 형변환</h3>
<p><strong>int &lt; long &lt; double</strong></p>
<pre><code class="language-java">package method;

public class MethodCasting2 {
    public static void main(String[] args) {
        int number = 100;
        printNumber(number); // int에서 double로 자동 형변환
    }
    public static void printNumber(double n) {
        System.out.println(&quot;숫자: &quot; + n);
    }
}</code></pre>
<h2 id="메서드-오버로딩">메서드 오버로딩</h2>
<pre><code class="language-java">add(int a, int b)
add(int a, int b, int c)
add(double a, double b)</code></pre>
<p>=&gt; 이름이 같고 매개변수가 다른 메서드를 여러개 정의하는 것을 메서드 오버로딩(Overloading)이라 한다.</p>
<h3 id="오버로딩-규칙">오버로딩 규칙</h3>
<p>메서드의 이름이 같아도 매개변수의 타입 및 순서가 다르면 오버로딩을 할 수 있다. 참고로 반환 타입은 인정하지 않는다.
&lt;오버로딩 실패&gt;</p>
<pre><code class="language-java">int add(int a, int b)
double add(int a, int b)</code></pre>
<p>&lt;OverLoading1.java&gt;</p>
<pre><code class="language-java">package method;

public class OverLoading1 {
    public static void main(String[] args) {
        System.out.println(&quot;1: &quot; + add(1, 2));
        System.out.println(&quot;2: &quot; + add(1, 2, 3));
    }

    // 첫 번째 add 메서드: 두 정수를 받아서 합을 반환한다.
    public static int add(int a, int b) {
        System.out.println(&quot;1번 호출&quot;);
        return a + b;
    }

    // 두 번째 add 메서드: 세 정수를 받아서 합을 반환한다.
    // 첫 번째 메서드와 이름은 같지만, 매개변수 목록이 다르다.
    public static int add(int a, int b, int c) {
        System.out.println(&quot;2번 호출&quot;);
        return a + b + c;
    }

}</code></pre>
<p>&lt;OverLoading2.java&gt;</p>
<pre><code class="language-java">package method;

public class OverLoading2 {
    public static void main(String[] args) {
        myMethod(1, 1.2);
        myMethod(1.2, 2);
    }
    public static void myMethod(int a, double b) {
        System.out.println(&quot;int a, double b&quot;);
    }
    public static void myMethod(double a, int b) {
        System.out.println(&quot;double a, int b&quot;);
    }
}</code></pre>
<p>&lt;OverLoading3.java&gt;</p>
<pre><code class="language-java">package method;

public class OverLoading3 {
    public static void main(String[] args) {
        System.out.println(&quot;1: &quot; + add(1, 2));
        System.out.println(&quot;2: &quot; + add(1.2, 1.5));
    }

    // 첫 번째 add 메서드: 두 정수를 받아서 합을 반환한다.
    public static int add(int a, int b) {
        System.out.println(&quot;1번 호출&quot;);
        return a + b;
    }

    // 두 번째 add 메서드: 두 실수를 받아서 합을 반환한다.
    // 첫 번째 메서드와 이름은 같지만, 매개변수의 유형이 다르다.
    public static double add(double a, double b) {
        System.out.println(&quot;2번 호출&quot;);
        return a + b;
    }

    //첫번째 add 메서드를 지운다 해도 실행이된다. 
    // int 거 double 로 자동 형변환이 이루어지기 때문에!
}</code></pre>
<h2 id="문제와-풀이1">문제와 풀이1</h2>
<p>&lt;MethodEx1.java&gt;</p>
<pre><code class="language-java">package method.ex;

public class MethodEx1 {
    public static void main(String[] args) {
        double average = avg(1,2,3);
        System.out.println(&quot;평균값: &quot; + average);

        System.out.println(&quot;평균값: &quot; + avg(15,25,35));
    }

    public static double avg(int a, int b, int c){
        int sum = a+b+c;
        double average = sum / 3.0;
        return average;
    }
}</code></pre>
<p>&lt;MethodEx2.java&gt;</p>
<pre><code class="language-java">package method.ex;

public class MethodEx2 {
    public static void main(String[] args) {
       comment(3);
        System.out.println(&quot;----&quot;);
       comment(5);
        System.out.println(&quot;----&quot;);
       comment(7);
    }

    public static void comment(int num){
        String message = &quot;Hello, world!&quot;;
        for(int i =0; i&lt;num; i++){
            System.out.println(message);
        }
    }
//
//    public static void printMessage(String message, int times) {
//        for (int i = 0; i &lt; times; i++) {
//            System.out.println(message);
//        }
//    }
}</code></pre>
<p>&lt;MethodEx3.java&gt;</p>
<pre><code class="language-java">package method.ex;

public class MethodEx3 {
    public static void main(String[] args) {
        int balance = 10000;
        balance = deposit(balance, 1000);
        balance = withdraw(balance,2000);

        System.out.println(&quot;최종 잔액: &quot; + balance + &quot;원&quot;);
    }
    public static int deposit(int balance,int amount){
        balance+=amount;
        System.out.println(amount + &quot;원을 입금하였습니다. 현재 잔액: &quot; + balance
                + &quot;원&quot;);
        return balance;
    }
    public static int withdraw(int balance,int amount){
        if (balance &gt;= amount) {
            balance -= amount;
            System.out.println(amount + &quot;원을 출금하였습니다. 현재 잔액: &quot; +
                    balance + &quot;원&quot;);
        } else {
            System.out.println(amount + &quot;원을 출금하려 했으나 잔액이 부족합니다.&quot;);
        }
        return balance;
    }
}</code></pre>
<h2 id="문제와-풀이2">문제와 풀이2</h2>
<p>&lt;MethodEx4.java&gt;</p>
<pre><code class="language-java">package method.ex;

import java.util.Scanner;

public class MethodEx4 {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int balance = 0;

        while(true){
            System.out.print(&quot;선택 : &quot;);
            int option = input.nextInt();

//            if(option==1){
//                System.out.print(&quot;입금액 : &quot;);
//                int depositamount = input.nextInt();
//                balance = deposit(balance,depositamount);
//            } else if (option ==2) {
//                System.out.print(&quot;출금액 : &quot;);
//                int withdrawAmount = input.nextInt();
//                balance = withdraw(balance,withdrawAmount);
//            } else if (option ==3) {
//                System.out.println(&quot;현재 잔액 : &quot; +balance);
//            } else if (option==4) {
//                System.out.println(&quot;시스템을 종료합니다.&quot;);
//                break;
//            }

            int amount;
            switch (option){
                case 1 :
                    System.out.print(&quot;입금액 : &quot;);
                    amount = input.nextInt();
                    balance = deposit(balance,amount);
                    break;
                case 2 :
                    System.out.print(&quot;입금액 : &quot;);
                    amount = input.nextInt();
                    balance = withdraw(balance,amount);
                    break;
                case 3:
                    System.out.println(&quot;현재 잔액: &quot; + balance + &quot;원&quot;);
                    break;
                case 4:
                    System.out.println(&quot;시스템을 종료합니다.&quot;);
                    return;
                default:
                    System.out.println(&quot;올바른 선택이 아닙니다. 다시 선택해주세요.&quot;);
            }
        }

    }
    public static int deposit(int balance,int amount){
        balance+=amount;
        System.out.println(amount + &quot;원을 입금하였습니다. 현재 잔액: &quot; + balance
                + &quot;원&quot;);
        return balance;
    }
    public static int withdraw(int balance,int amount){
        if (balance &gt;= amount) {
            balance -= amount;
            System.out.println(amount + &quot;원을 출금하였습니다. 현재 잔액: &quot; +
                    balance + &quot;원&quot;);
        } else {
            System.out.println(amount + &quot;원을 출금하려 했으나 잔액이 부족합니다.&quot;);
        }
        return balance;
    }
}</code></pre>
<h2 id="정리">정리</h2>
<ul>
<li>메서드명은 일반적으로 동사로 시작한다.</li>
<li>메서드 사용의 장점<blockquote>
<ul>
<li>코드 재사용 : 메서드는 특정 기능을 캡슐화하므로 필요할 때 마다 사용가능</li>
<li>코드 가독성</li>
<li>모듈성 : 큰 프로젝트, 관리 가능한 부분으로 나눌 수 있다. 이는 코드의 가독성향상 및 디버깅을 쉽게 만든다.</li>
<li>코드유지관리 </li>
<li>코드 재사용성과 확장성</li>
<li>추상화 : 프로그램의 다른 부분에서는 복잡한 내부작업에 대해 알 필요가 없다.</li>
<li>테스트와 디버깅 용이성 : 개별 메서드는 독립적으로 테스트하고 디버그 할 수 있다. 이는 신속하게 찾고 수정하는데 도움이 된다.</li>
</ul>
</blockquote>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[JAVA 멍충이 탈출기 7]]></title>
            <link>https://velog.io/@baby_potato/JAVA-%EB%A9%8D%EC%B6%A9%EC%9D%B4-%ED%83%88%EC%B6%9C%EA%B8%B0-7</link>
            <guid>https://velog.io/@baby_potato/JAVA-%EB%A9%8D%EC%B6%A9%EC%9D%B4-%ED%83%88%EC%B6%9C%EA%B8%B0-7</guid>
            <pubDate>Mon, 08 Apr 2024 08:14:04 GMT</pubDate>
            <description><![CDATA[<h1 id="배열">배열</h1>
<h2 id="배열시작">배열시작</h2>
<h3 id="배열이-필요한-이유">배열이 필요한 이유</h3>
<p>같은 타입의 변수를 반복해서 선언하고 반복해서 사용하는 문제를 해결하는 것이 바로 배열이다.</p>
<h2 id="배열의-선언과-생성">배열의 선언과 생성</h2>
<p>&lt;ArrayRef1.java&gt;</p>
<pre><code class="language-java">package array;

public class ArrayRef1 {
    public static void main(String[] args) {
        int[] students; //배열 변수 선언
        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]);

        System.out.println(students);
    }
}</code></pre>
<h2 id="배열사용">배열사용</h2>
<h3 id="인덱스">인덱스</h3>
<ul>
<li>배열은 0부터 시작한다.</li>
</ul>
<h3 id="기본현-vs-참조형">기본현 vs 참조형</h3>
<p>사용하는 값을 직접 넣을 수 있는 기본형, 배열 변수와 같이 메모리의 참조값을 넣을 수 있는 참조형</p>
<blockquote>
<ul>
<li>기본형(Primitive Type): 우리가 지금까지 봤던 int , long , double , boolean 처럼 변수에 사용할 값을 직접 넣을 수 있는 데이터 타입을 기본형(Primitive Type)이라 한다.</li>
</ul>
</blockquote>
<ul>
<li>참조형(Reference Type): int[] students 와 같이 데이터에 접근하기 위한 참조(주소)를 저장하는 데이터타입을 참조형(Reference Type)이라 한다. 뒤에서 학습하는 객체나 클래스를 담을 수 있는 변수들도 모두 참조형이다.</li>
</ul>
<h2 id="배열-리펙토링">배열 리펙토링</h2>
<blockquote>
<p>리펙토링(Refactoring)은 기존의 코드 기능은 유지하면서 내부 구조를 개선하여 가독성을 높이고, 유지보수를 용이하게 하는 과정을 뜻한다. 이는 중복을 제거하고, 복잡성을 줄이며, 이해하기 쉬운 코드로 만들기 위해 수행된다. 리펙토링은 버그를 줄이고, 프로그램의 성능을 향상시킬 수도 있으며, 코드의 설계를 개선하는 데에도 도움이 된다.
쉽게 이야기해서 작동하는 기능은 똑같은데, 코드를 개선하는 것을 리펙토링이라 한다</p>
</blockquote>
<p>&lt;ArrayRef2.java&gt;</p>
<pre><code class="language-java">package array;

public class ArrayRef2 {
    public static void main(String[] args) {
        int[] students; //배열 변수 선언
        students = new int[5]; // 배열생성
        students[0]=90;
        students[1] = 80;
        students[2] = 70;
        students[3] = 60;
        students[4] = 50;

        for(int i =0;i&lt;students.length;i++){
            System.out.println(&quot;학생&quot;+(i+1)+ &quot; 점수 : &quot;+ students[i]);
        }

        System.out.println(students);
    }
}</code></pre>
<ul>
<li>반복문을 사용하여 효과적으로 변경</li>
</ul>
<p>&lt;ArrayRef3.java&gt;</p>
<pre><code class="language-java">package array;

public class ArrayRef3 {
    public static void main(String[] args) {
        int[] students;

        students = new int[]{90, 80, 70, 60, 50}; //배열 생성과 초기화

        for (int i = 0; i &lt; students.length; i++) {
            System.out.println(&quot;학생&quot; + (i + 1) + &quot; 점수: &quot; + students[i]);
        }
    }
}</code></pre>
<ul>
<li>배열 생성과 초기화를 한줄로도 표현이 가능하다.</li>
</ul>
<p>&lt;ArrayRef4.java&gt;</p>
<pre><code class="language-java">package array;

public class ArrayRef4 {
    public static void main(String[] args) {
        //배열 생성 간략 버전, 배열 선언과 함께 사용시 new int[] 생략 가능
        int[] students = {90, 80, 70, 60, 50};
        for (int i = 0; i &lt; students.length; i++) {
            System.out.println(&quot;학생&quot; + (i + 1) + &quot; 점수: &quot; + students[i]);
        }
    }
}</code></pre>
<blockquote>
<p>int[] students;
students = {90, 80, 70, 60, 50};
 =&gt; <strong>오류</strong></p>
</blockquote>
<h2 id="2차원-배열---시작">2차원 배열 - 시작</h2>
<p>2차원 배열은 행과 열로 구성된다.</p>
<blockquote>
<p>arr[행][열] , arr[row][column]</p>
</blockquote>
<p>&lt;ArrayDi0.java&gt;</p>
<pre><code class="language-java">package array;

public class ArrayDi0 {
    public static void main(String[] args) {
        int[][] arr = new int[2][3];

        arr[0][0] = 1; //0행, 0열
        arr[0][1] = 2; //0행, 1열
        arr[0][2] = 3; //0행, 2열
        arr[1][0] = 4; //1행, 0열
        arr[1][1] = 5; //1행, 1열
        arr[1][2] = 6; //1행, 2열
        //0행 출력
        System.out.print(arr[0][0] + &quot; &quot;); //0열 출력
        System.out.print(arr[0][1] + &quot; &quot;); //1열 출력
        System.out.print(arr[0][2] + &quot; &quot;); //2열 출력
        System.out.println(); //한 행이 끝나면 라인을 변경한다.

        //1행 출력
        System.out.print(arr[1][0] + &quot; &quot;); //0열 출력
        System.out.print(arr[1][1] + &quot; &quot;); //1열 출력
        System.out.print(arr[1][2] + &quot; &quot;); //2열 출력
        System.out.println(); //한 행이 끝나면 라인을 변경한다
    }
}</code></pre>
<h2 id="2차원-배열---리펙토링1">2차원 배열 - 리펙토링1</h2>
<p>for문을 활용하여 출력방법 개선하기
&lt;ArrayDi1.java&gt;</p>
<pre><code class="language-java">package array;

public class ArrayDi1 {
    public static void main(String[] args) {
        int[][] arr = new int[2][3];

        arr[0][0] = 1; //0행, 0열
        arr[0][1] = 2; //0행, 1열
        arr[0][2] = 3; //0행, 2열
        arr[1][0] = 4; //1행, 0열
        arr[1][1] = 5; //1행, 1열
        arr[1][2] = 6; //1행, 2열

        for (int row = 0; row &lt; 2; row++) {
            System.out.print(arr[row][0] + &quot; &quot;); //0열 출력
            System.out.print(arr[row][1] + &quot; &quot;); //1열 출력
            System.out.print(arr[row][2] + &quot; &quot;); //2열 출력
            System.out.println(); //한 행이 끝나면 라인을 변경한다.
        }
    }
}</code></pre>
<p>&lt;ArrayDi2.java&gt;</p>
<pre><code class="language-java">package array;

public class ArrayDi2 {
    public static void main(String[] args) {
        int[][] arr = new int[2][3];

        arr[0][0] = 1; //0행, 0열
        arr[0][1] = 2; //0행, 1열
        arr[0][2] = 3; //0행, 2열
        arr[1][0] = 4; //1행, 0열
        arr[1][1] = 5; //1행, 1열
        arr[1][2] = 6; //1행, 2열

        for (int row = 0; row &lt; 2; row++) {
            for (int column = 0; column &lt; 3; column++) {
                System.out.print(arr[row][column] + &quot; &quot;);
            }
            System.out.println(); //한 행이 끝나면 라인을 변경한다.
        }
    }
}</code></pre>
<h2 id="2차원-배열---리펙토링1-1">2차원 배열 - 리펙토링1</h2>
<p>2차원배열도 쉽게 초기화 할 수 있다.
for문에서 배열의 길이 활용하기
&lt;ArrayDi3.java&gt;</p>
<pre><code class="language-java">package array;

public class ArrayDi3 {
    public static void main(String[] args) {
        // 2x3 2차원 배열, 초기화
        int[][] arr = {
                {1, 2, 3},
                {4, 5, 6}
        };

        // 2차원 배열의 길이를 활용
        for (int row = 0; row &lt; arr.length; row++) {
            for (int column = 0; column &lt; arr[row].length; column++) {
                System.out.print(arr[row][column] + &quot; &quot;);
            }
            System.out.println();
        }
    }
}</code></pre>
<h3 id="구조개선---값입력">구조개선 - 값입력</h3>
<p>&lt;ArrayDi4.java&gt;</p>
<pre><code class="language-java">package array;

public class ArrayDi4 {
    public static void main(String[] args) {
        // 2x3 2차원 배열, 초기화
        int[][] arr = new int[2][3];
        int i = 1;
        // 순서대로 1씩 증가하는 값을 입력한다.
        for (int row = 0; row &lt; arr.length; row++) {
            for (int column = 0; column &lt; arr[row].length; column++) {
                arr[row][column] = i++;
            }
        }
        // 2차원 배열의 길이를 활용
        for (int row = 0; row &lt; arr.length; row++) {
            for (int column = 0; column &lt; arr[row].length; column++) {
                System.out.print(arr[row][column] + &quot; &quot;);
            }
            System.out.println();
        }
    }
}</code></pre>
<h2 id="향상된-for문">향상된 for문</h2>
<blockquote>
<p>for (변수 : 배열 또는 컬렉션) {
 // 배열 또는 컬렉션의 요소를 순회하면서 수행할 작업
}</p>
</blockquote>
<p>&lt;EnhancedFor1.java&gt;</p>
<pre><code class="language-java">package array;

public class EnhancedFor1 {
    public static void main(String[] args) {
        int[] numbers ={1,2,3,4,5};

        //일반 for문
        for(int i=0; i&lt;numbers.length;i++){
            int number = numbers[i];
            System.out.println(number);
        }

        //향상된 for문
        for(int number:numbers){
            System.out.println(number);
        }
    }
}</code></pre>
<ul>
<li>향상된 for문을 사용하지 못하는 경우
=증가하는 인덱스 값을 직접 사용해야하는 경우</li>
</ul>
<h2 id="문제와-풀이1">문제와 풀이1</h2>
<p>&lt;ArrayEx1.java&gt;</p>
<pre><code class="language-java">package array.ex;

public class ArrayEx1 {
    public static void main(String[] args) {
        int[] students = {90,80,70,60,50};
        int total = 0;

        for (int i = 0; i &lt; students.length; i++) {
            total += students[i];
        }
        double avg = (double) total / students.length;
        System.out.println(&quot;total: &quot;+ total);
        System.out.println(&quot;avg: &quot;+avg);
    }
}</code></pre>
<p>&lt;ArrayEx2.java&gt;</p>
<pre><code class="language-java">package array.ex;

import java.util.Scanner;

public class ArrayEx2 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int[] arr = new int[5] ;
        for(int i = 0; i&lt; arr.length; i++){
            arr[i] = scanner.nextInt();
        }
        for (int i=0; i&lt;arr.length;i++){
            System.out.print(arr[i]);
            if(i&lt; arr.length-1){
                System.out.print(&quot;,&quot;);
            }
        }
    }
}</code></pre>
<p>&lt;ArrayEx3.java&gt;</p>
<pre><code class="language-java">package array.ex;

import java.util.Scanner;

public class ArrayEx3 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int[] arr = new int[5] ;
        for(int i = 0; i&lt; arr.length; i++){
            arr[i] = scanner.nextInt();
        }
        System.out.print(&quot;기존 배열 : &quot;);
        for (int i=0; i&lt;arr.length;i++){
            System.out.print(arr[i]);
            if(i&lt; arr.length-1){
                System.out.print(&quot;,&quot;);
            }
        }
        System.out.println();
        System.out.print(&quot;역순 배열 : &quot;);
        for(int i =arr.length-1; i&gt;=0 ;i--){
            System.out.print(arr[i]);
            if(i&gt;0){
                System.out.print(&quot;,&quot;);
            }
        }
    }
}</code></pre>
<p>&lt;ArrayEx4.java&gt;</p>
<pre><code class="language-java">package array.ex;

import java.util.Scanner;

public class ArrayEx4 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int[] arr = new int[5] ;
        int sum =0;

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

        for (int i=0; i&lt;arr.length;i++){
            sum += arr[i];
        }
        System.out.println(&quot;합 : &quot; + sum);
        System.out.println(&quot;평균 : &quot;+ (double)sum / arr.length );

    }
}</code></pre>
<p>&lt;ArrayEx5.java&gt;</p>
<pre><code class="language-java">package array.ex;

import java.util.Scanner;

public class ArrayEx5 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.print(&quot;입력받을 숫자의 개수 : &quot;);
        int count = scanner.nextInt();

        int[] arr = new int[count] ;
        int sum =0;

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

        for (int i=0; i&lt;arr.length;i++){
            sum += arr[i];
        }
        System.out.println(&quot;합 : &quot; + sum);
        System.out.println(&quot;평균 : &quot;+ (double)sum / count );

    }
}</code></pre>
<h2 id="문제와-풀이2">문제와 풀이2</h2>
<p>&lt;ArrayEx5.java&gt;</p>
<pre><code class="language-java">package array.ex;

import java.util.Scanner;

public class ArrayEx5 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.print(&quot;입력받을 숫자의 개수 : &quot;);
        int count = scanner.nextInt();

        int[] arr = new int[count] ;

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

        int max = arr[0];
        int min = arr[0];

        for(int i =0;i&lt; arr.length;i++){
            if (arr[i] &lt; min) {
                min = arr[i];
            }
            if (arr[i] &gt; max) {
                max = arr[i];
            }
        }
        System.out.println(&quot;가장 작은 정수: &quot; + min);
        System.out.println(&quot;가장 큰 정수: &quot; + max);

    }
}</code></pre>
<p>&lt;ArrayEx7.java&gt;</p>
<pre><code class="language-java">package array.ex;

import java.util.Scanner;

public class ArrayEx7 {
    public static void main(String[] args) {
        int[][] arr = new int[4][3];
        String[] sub = {&quot;국어&quot;,&quot;영어&quot;,&quot;수학&quot;};
        Scanner scanner = new Scanner(System.in);


        for(int row=0; row&lt;arr.length; row++){
            System.out.println((row+1)+&quot;번 학생의 성적을 입력하세요 : &quot;);
            for(int col=0; col&lt; arr[row].length;col++){
                System.out.print(sub[col]+ &quot; : &quot;);
                arr[row][col] = scanner.nextInt();
            }
        }

        for(int row=0; row&lt; arr.length; row++){
            int sum =0;
            for(int col=0; col&lt;arr[row].length;col++){
                sum += arr[row][col];
            }
            System.out.println((row+1)+ &quot;번 학생의 총점 : &quot;+sum);
            System.out.println((row+1)+ &quot;번 학생의 평균 : &quot;+ ((double)sum / sub.length));
        }

    }
}</code></pre>
<p>&lt;ArrayEx8.java&gt;</p>
<pre><code class="language-java">package array.ex;

import java.util.Scanner;

public class ArrayEx8 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.print(&quot;학생 수 입력 : &quot;);
        int count = scanner.nextInt();
        int[][] arr = new int[count][3];
        String[] sub = {&quot;국어&quot;,&quot;영어&quot;,&quot;수학&quot;};

        for(int row=0; row&lt;arr.length; row++){
            System.out.println((row+1)+&quot;번 학생의 성적을 입력하세요 : &quot;);
            for(int col=0; col&lt; arr[row].length;col++){
                System.out.print(sub[col]+ &quot; : &quot;);
                arr[row][col] = scanner.nextInt();
            }
        }

        for(int row=0; row&lt; arr.length; row++){
            int sum =0;
            for(int col=0; col&lt;arr[row].length;col++){
                sum += arr[row][col];
            }
            System.out.println((row+1)+ &quot;번 학생의 총점 : &quot;+sum);
            System.out.println((row+1)+ &quot;번 학생의 평균 : &quot;+ ((double)sum / sub.length));
        }

    }
}</code></pre>
<h2 id="문제와-풀이3">문제와 풀이3</h2>
<p>&lt;ProductAdminEx.java&gt;</p>
<pre><code class="language-java">package array.ex;

import java.util.Scanner;

public class ProductAdminEx {
    public static void main(String[] args) {
        int maxProducts = 10;
        String[] productNames = new String[maxProducts];
        int[] productPrices = new int[maxProducts];
        int productCount = 0;
        Scanner scanner = new Scanner(System.in);

        while (true) {
            System.out.print(&quot;1. 상품 등록 | 2. 상품 목록 | 3. 종료\n메뉴를 선택하세요:&quot;);
            int menu = scanner.nextInt();
            scanner.nextLine();
            if (menu == 1) {
                if (productCount &gt;= maxProducts) {
                    System.out.println(&quot;더 이상 상품을 등록할 수 없습니다.&quot;);
                    continue;
                }
                System.out.print(&quot;상품 이름을 입력하세요:&quot;);
                productNames[productCount] = scanner.nextLine();
                System.out.print(&quot;상품 가격을 입력하세요:&quot;);
                productPrices[productCount] = scanner.nextInt();
                productCount++;
            } else if (menu == 2) {
                if (productCount == 0) {
                    System.out.println(&quot;등록된 상품이 없습니다.&quot;);
                    continue;
                }
                for (int i = 0; i &lt; productCount; i++) {
                    System.out.println(productNames[i] + &quot;: &quot; + productPrices[i]
                            + &quot;원&quot;);
                }
            } else if (menu == 3) {
                System.out.println(&quot;프로그램을 종료합니다.&quot;);
                break;
            } else {
                System.out.println(&quot;잘못된 메뉴를 선택하셨습니다.&quot;);
            }
        }
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[JAVA 멍충이 탈출기 6]]></title>
            <link>https://velog.io/@baby_potato/JAVA-%EB%A9%8D%EC%B6%A9%EC%9D%B4-%ED%83%88%EC%B6%9C%EA%B8%B0-6</link>
            <guid>https://velog.io/@baby_potato/JAVA-%EB%A9%8D%EC%B6%A9%EC%9D%B4-%ED%83%88%EC%B6%9C%EA%B8%B0-6</guid>
            <pubDate>Mon, 08 Apr 2024 04:38:52 GMT</pubDate>
            <description><![CDATA[<h1 id="훈련">훈련</h1>
<h2 id="scanner">Scanner</h2>
<p>System.out 을 통해서 출력을 했듯이, System.in 을 통해서 사용자의 입력을 받을 수 있다.
System.in 을 통해서 사용자 입력을 받으려면 여러 과정을 거쳐야해서 복잡하고 어렵다.
자바는 이런 문제를 해결하기 위해 <strong>Scanner</strong> 라는 클래스를 제공한다.
&lt;Scanner1.java&gt;</p>
<pre><code class="language-java">package scanner;

import java.util.Scanner;

public class Scanner1 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.println(&quot;문자열을 입력하세요 : &quot;);
        String str = scanner.nextLine();
        System.out.println(&quot;입력한 문자열 : &quot;+str);

        System.out.println(&quot;정수를 입력하세요 : &quot;);
        int intV = scanner.nextInt();
        System.out.println(&quot;입력한 정수 : &quot;+intV);

        System.out.println(&quot;실수를 입력하세요 : &quot;);
        double doubleV = scanner.nextDouble();
        System.out.println(&quot;입력한 실수 : &quot;+doubleV);
    }
}</code></pre>
<blockquote>
<ul>
<li>scanner.nextLine()
엔터( \n )을 입력할 때 까지 문자를 가져온다.</li>
</ul>
</blockquote>
<p>&lt;결과&gt;
<img src="https://velog.velcdn.com/images/baby_potato/post/c3d3346b-e52d-4720-b4e8-c63f00ebe693/image.png" alt=""></p>
<blockquote>
<p><strong>타입이 다르면 오류가 발생한다. 예제와 같이 숫자에 문자를 입력하면 오류가 발생한다</strong></p>
</blockquote>
<h3 id="print-vs-println">print() vs printLn()</h3>
<blockquote>
<p>print() 출력하고 다음라인으로 넘기지 않는다.
println() 출력하고 다음라인으로 넘긴다.</p>
</blockquote>
<h2 id="scanner-기본-예제">Scanner 기본 예제</h2>
<p>&lt;Scanner2.java&gt;</p>
<pre><code class="language-java">package scanner;

import java.util.Scanner;

public class Scanner2 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.print(&quot;첫 번째 숫자를 입력하세요:&quot;);
        int num1 = scanner.nextInt();

        System.out.print(&quot;두 번째 숫자를 입력하세요:&quot;);
        int num2 = scanner.nextInt();

        int sum = num1 + num2;
        System.out.println(&quot;두 숫자의 합: &quot; + sum);
    }
}</code></pre>
<p>&lt;Scanner3.java&gt;</p>
<pre><code class="language-java">package scanner;

import java.util.Scanner;

public class Scanner3 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int num1 = scanner.nextInt();
        int num2 = scanner.nextInt();

        if(num1 &gt; num2){
            System.out.println(&quot;num1이 더 크다.&quot;);
        }
        else if(num2 &gt; num1){
            System.out.println(&quot;num2이 더 크다.&quot;);
        }else if(num2 == num1){
            System.out.println(&quot; 두수가 같다.&quot;);
        }
    }
}</code></pre>
<h2 id="scanner-반복예제">Scanner 반복예제</h2>
<p>&lt;ScannerWhile1.java&gt;</p>
<pre><code class="language-java">package scanner;

import java.util.Scanner;

public class ScannerWhile1 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        while(true){
            System.out.println(&quot;문자열을 입력하세요(exit: 종료):&quot;);
            String str = scanner.nextLine();
            if(str.equals(&quot;exit&quot;)){
                System.out.println(&quot;종료&quot;);
                break;
            }
        }
    }
}</code></pre>
<p>&lt;결과&gt;
<img src="https://velog.velcdn.com/images/baby_potato/post/8be5fa6f-2722-438c-a06f-037795cfcb95/image.png" alt="">
&lt;Scanner3.java&gt;</p>
<pre><code class="language-java">package scanner;

import java.util.Scanner;

public class ScannerWhile2 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        while(true){
        int num1 = scanner.nextInt();
        int num2 = scanner.nextInt();

        if(num1==0 &amp;&amp; num2==0){
            break;
        }else {
            int sum = num1+num2;
            System.out.println(sum);
        }
        }
    }
}</code></pre>
<p>&lt;ScannerWhile3.java&gt;</p>
<pre><code class="language-java">package scanner;

import java.util.Scanner;

public class ScannerWhile3 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        int sum =0;

        while(true){
            int num = scanner.nextInt();
            if(num==0){
                break;
            }else {
                sum = sum + num;
            }
        }
        System.out.println(sum);
    }
}</code></pre>
<h2 id="문제와-풀이">문제와 풀이</h2>
<p>&lt;ScannerEx1.java&gt;</p>
<pre><code class="language-java">package scanner.ex;

import java.util.Scanner;

public class ScannerEx1 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        String name = scanner.nextLine();
        int age = scanner.nextInt();

        System.out.println(&quot;이름 : &quot;+name+ &quot; 나이 : &quot;+age);
    }
}</code></pre>
<p>&lt;ScannerEx2.java&gt;</p>
<pre><code class="language-java">package scanner.ex;

import java.util.Scanner;

public class ScannerEx2 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        int num = scanner.nextInt();
        if(num %2 ==0){
            System.out.println(&quot;짝수입니다&quot;);
        }else {
            System.out.println(&quot;홀수입니다&quot;);
        }
    }
}</code></pre>
<p>&lt;ScannerEx3.java&gt;</p>
<pre><code class="language-java">package scanner.ex;

import java.util.Scanner;

public class ScannerEx3 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        String foodName = scanner.nextLine();
        int foodPrice = scanner.nextInt();
        int foodQuantity = scanner.nextInt();

        int sum = foodPrice * foodQuantity;

        System.out.println(foodName + &quot; &quot; + foodQuantity+&quot;개를 주문하셨습니다.&quot;);
        System.out.println(&quot;총 가격은 &quot;+ sum +&quot;원 입니다.&quot;);

    }
}</code></pre>
<p>&lt;ScannerEx4.java&gt;</p>
<pre><code class="language-java">package scanner.ex;

import java.util.Scanner;

public class ScannerEx4 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        int num = scanner.nextInt();

        for (int i=1; i&lt;=9 ; i++){
            System.out.println(num + &quot; * &quot; + i +&quot; = &quot; + num+i);
        }
    }
}</code></pre>
<h2 id="문제와-풀이2">문제와 풀이2</h2>
<p>&lt;ChangeVarEx.java&gt;</p>
<pre><code class="language-java">package scanner.ex;

public class ChangeVarEx {
    public static void main(String[] args) {
        int a=10;
        int b=20;
        int temp;

        temp =a;
        a=b;
        b=temp;

        System.out.println(&quot;a : &quot; + a + &quot; b : &quot;+b);
    }
}</code></pre>
<p>&lt;ScannerEx5.java&gt;</p>
<pre><code class="language-java">package scanner.ex;

import java.util.Scanner;

public class ScannerEx5 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int num1 = scanner.nextInt();
        int num2 = scanner.nextInt();

        if(num1 &gt; num2){
            int temp;
            temp = num1;
            num1 =num2;
            num2 = temp;
        }

        for( int i = num1; i&lt;=num2; i++){
            System.out.print(i);
            if(i!=num2){
                System.out.print(&quot;,&quot;);
            }
        }
    }
}</code></pre>
<h2 id="문제와-풀이3">문제와 풀이3</h2>
<p>&lt;ScannerWhileEx1.java&gt;</p>
<pre><code class="language-java">package scanner.ex;

import java.util.Scanner;

public class ScannerWhileEx1 {
    public static void main(String[] args) {
       Scanner scanner = new Scanner(System.in);
       while(true){
           String name = scanner.nextLine();

           if(name.equals(&quot;종료&quot;)){
               System.out.println(&quot;프로그램을 종료합니다.&quot;);
               break;
           }
           int age = scanner.nextInt();
           scanner.nextLine();
           System.out.println(&quot;이름 : &quot;+ name + &quot; 나이 : &quot;+ age);

       }
    }
}</code></pre>
<blockquote>
<p>scanner.nextLine();
 숫자 입력 후 줄바꿈 처리</p>
</blockquote>
<p>&lt;ScannerWhileEx2.java&gt;</p>
<pre><code class="language-java">package scanner.ex;

import java.util.Scanner;

public class ScannerWhileEx2 {
    public static void main(String[] args) {
       Scanner scanner = new Scanner(System.in);
       while(true){
           int price = scanner.nextInt();

           if(price==-1){
               System.out.println(&quot;프로그램을 종료합니다.&quot;);
               break;
           }
           int quantity = scanner.nextInt();
           int sum = price*quantity;
           System.out.println(&quot;총비용 : &quot;+ sum );

       }
    }
}</code></pre>
<h2 id="문제와-풀이-1">문제와 풀이</h2>
<p>&lt;ScannerWhileEx3.java&gt;</p>
<pre><code class="language-java">package scanner.ex;

import java.util.Scanner;

public class ScannerWhileEx3 {
    public static void main(String[] args) {
       Scanner scanner = new Scanner(System.in);
        int sum =0;
        double average=0;
        int count =0;

       while(true){
           int num = scanner.nextInt();

           if(num==-1) {
               System.out.println(&quot;프로그램을 종료합니다.&quot;);
               break;
           }
           sum += num;
           count ++;

       }
        average = (double) sum / count;
        System.out.println(&quot;sum : &quot;+sum +&quot; average : &quot;+average);
    }
}</code></pre>
<p>&lt;ScannerWhileEx4.java&gt;</p>
<pre><code class="language-java">package scanner.ex;

import java.util.Scanner;

public class ScannerWhileEx4 {
    public static void main(String[] args) {
       Scanner scanner = new Scanner(System.in);
        int total=0;
        while(true){
            int option = scanner.nextInt();
            if (option == 1) {
                scanner.nextLine();

                System.out.print(&quot;상품명을 입력하세요: &quot;);
                String product = scanner.nextLine();
                System.out.print(&quot;상품의 가격을 입력하세요: &quot;);
                int price = scanner.nextInt();
                System.out.print(&quot;구매 수량을 입력하세요: &quot;);
                int quantity = scanner.nextInt();
                total = price * quantity;

                System.out.println(&quot;상품명:&quot; + product + &quot; 가격:&quot; + price + &quot; 수량:&quot; + quantity + &quot; 합계:&quot; + total);

            } else if (option == 2) {
                System.out.println(&quot;총 비용 : &quot; + total);
                total = 0;
            } else if (option == 3) {
                System.out.println(&quot;프로그램을 종료합니다.&quot;);
                break;
            } else {
                System.out.println(&quot;올바른 옵션 값을 입력해주세요.&quot;);
            }
        }
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[JAVA 멍충이 탈출기 5]]></title>
            <link>https://velog.io/@baby_potato/JAVA-%EB%A9%8D%EC%B6%A9%EC%9D%B4-%ED%83%88%EC%B6%9C%EA%B8%B0-4-14jzt7dp</link>
            <guid>https://velog.io/@baby_potato/JAVA-%EB%A9%8D%EC%B6%A9%EC%9D%B4-%ED%83%88%EC%B6%9C%EA%B8%B0-4-14jzt7dp</guid>
            <pubDate>Thu, 04 Apr 2024 05:46:59 GMT</pubDate>
            <description><![CDATA[<h2 id="스코프1---지역-변수와-스코프">스코프1 - 지역 변수와 스코프</h2>
<p>변수는 선언한 위치에 따라 지역 변수, 멤버 변수(클래스 변수, 인스턴스 변수)와 같이 분류된다.
지역 변수는 이름 그대로 특정 지역에서만 사용할 수 있는 변수라는 뜻이다. 그 특정 지역을 벗어나면 사용할 수 없다.
여기서 말하는 지역이 바로 변수가 선언된 <strong>코드 블록( {} )</strong> 이다. 지역 변수는 자신이 선언된 코드 블록( {} ) 안에서만 생
존하고, 자신이 선언된 코드 블록을 벗어나면 제거된다.</p>
<p>&lt;Scope1.java&gt;</p>
<pre><code class="language-java">package scope;

public class Scope1 {
    public static void main(String[] args) {
        int m = 10; //m 생존 시작
        if (true) {
            int x = 20; //x 생존 시작
            System.out.println(&quot;if m = &quot; + m); //블록 내부에서 블록 외부는 접근 가능
            System.out.println(&quot;if x = &quot; + x);
        } //x 생존 종료
        //System.out.println(&quot;main x = &quot; + x); //오류, 변수 x에 접근 불가
        System.out.println(&quot;main m = &quot; + m);
    } //m 종료
}</code></pre>
<blockquote>
<ul>
<li>int m 
=&gt; main{}의 코드 블록안에서 선언 , if{}블록 내부에서도 m 에 접근가능</li>
</ul>
</blockquote>
<ul>
<li>int x 
=&gt; if{} 블록안에서 선언</li>
</ul>
<blockquote>
<p>변수의 접근 가능한 범위를 <strong>스코프(Scope)</strong> 라 한다.</p>
</blockquote>
<p>&lt;Scope2.java&gt;</p>
<pre><code class="language-java">package scope;

public class Scope2 {
    public static void main(String[] args) {
        int m = 10;
        for (int i = 0; i &lt; 2; i++) { //블록 내부, for문 내
            System.out.println(&quot;for m = &quot; + m); //블록 내부에서 외부는 접근 가능
            System.out.println(&quot;for i = &quot; + i);
        } //i 생존 종료
        //System.out.println(&quot;main i = &quot; + i); //오류, i에 접근 불가
        System.out.println(&quot;main m = &quot; + m);
    }
}</code></pre>
<h2 id="스코프2---스코프-존재-이유">스코프2 - 스코프 존재 이유</h2>
<p>&lt;Scope3_1.java&gt;</p>
<pre><code class="language-java">package scope;

public class Scope3_1 {
    public static void main(String[] args) {
        int m=10;
        int temp=0;
        if(m&gt;0){
            temp = m*2;
            System.out.println(&quot;temp = &quot; +temp);
        }
        System.out.println(&quot;m = &quot;+m);
    }
}</code></pre>
<p>=&gt; temp 는 if 조건이 만족할 때 임시로 잠깐 사용하는 변수이다. 그런데 임시 변수를 main()에 선언하기 되면 다음과 같은 문제가 발생한다.</p>
<ul>
<li>비효율적인 메모리 사용 </li>
<li>코드 복잡성 증가 
&lt;Scope3_2.java&gt;로 보완<pre><code class="language-java">package scope;
</code></pre>
</li>
</ul>
<p>public class Scope3_2 {
    public static void main(String[] args) {
        int m = 10;
        if (m &gt; 0) {
            int temp = m * 2;
            System.out.println(&quot;temp = &quot; + temp);
        }
        System.out.println(&quot;m = &quot; + m);
    }
}</p>
<pre><code>
## 형변환1 - 자동 형변환
### 형변환
* 작은 범위에서 큰 범위로는 당연히 값을 넣을 수 있다.
예) int -&gt; long -&gt; double
    int 보다는 long 이, long 보다는 double 이 더 큰 범위를 표현할 수 있다.
* 큰 범위에서 작은 범위는 다음과 같은 문제가 발생할 수 있다.
-소수점 버림
-오버플로우

&lt;Casting1.java&gt;
-&gt; 작은 범위에서 큰 범위에 값을 대입
```java
package scope;

public class Casting1 {
    public static void main(String[] args) {
        int intValue = 10;
        long longValue;
        double doubleValue;

        longValue = intValue; // int -&gt; long
        System.out.println(&quot;longValue = &quot; + longValue); //longValue = 10

        doubleValue = intValue; // int -&gt; double
        System.out.println(&quot;doubleValue1 = &quot; + doubleValue); //doubleValue1 =10.0

        doubleValue = 20L; // long -&gt; double
        System.out.println(&quot;doubleValue2 = &quot; + doubleValue); //doubleValue2 =20.0
    }
}</code></pre><p>&lt;결과&gt;
<img src="https://velog.velcdn.com/images/baby_potato/post/8a83a264-2feb-47c9-a2d9-7ac653b410e5/image.png" alt=""></p>
<h3 id="자동-형변환-묵시적-형변환">자동 형변환 (묵시적 형변환)</h3>
<pre><code class="language-java">//intValue = 10
doubleValue = intValue
doubleValue = (double) intValue //형 맞추기
doubleValue = (double) 10 //변수 값 읽기
doubleValue = 10.0 //형변환</code></pre>
<p>=&gt; (double)과 같이 적어주면 int형이 double형으로 변한다.</p>
<h2 id="형변환2---명시적-형변환">형변환2 - 명시적 형변환</h2>
<p>큰 범위에서 작은 범위 대입은 명시적 형변환이 필요하다.
double-&gt; int ?
&lt;Casting2.java&gt;</p>
<pre><code class="language-java">package scope;

public class Casting2 {
    public static void main(String[] args) {
        double doubleValue = 1.5;
        int intValue = 0;

        //intValue = doubleValue; //컴파일 오류 발생
        intValue = (int) doubleValue; //형변환
        System.out.println(intValue); //출력:1

    }
}</code></pre>
<h3 id="명시적-형변환-과정">명시적 형변환 과정</h3>
<pre><code class="language-java">//doubleValue = 1.5
intValue = (int) doubleValue;
intValue = (int) 1.5; //doubleValue에 있는 값을 읽는다.
intValue = 1; //(int)로 형변환 한다. intValue에 int형인 숫자 1을 대입한다.</code></pre>
<h3 id="형변환과-오버플로우">형변환과 오버플로우</h3>
<p>형변환을 할 때 작은숫자가 표현할수 있는 범위를 넘어서면 어떻게 될까?
&lt;Casting3.java&gt;</p>
<pre><code class="language-java">package scope;

public class Casting3 {
    public static void main(String[] args) {
        long maxIntValue = 2147483647; //int 최고값
        long maxIntOver = 2147483648L; //int 최고값 + 1(초과)
        int intValue = 0;

        intValue = (int) maxIntValue; //형변환
        System.out.println(&quot;maxIntValue casting=&quot; + intValue); //출력:2147483647

        intValue = (int) maxIntOver; //형변환
        System.out.println(&quot;maxIntOver casting=&quot; + intValue); //출력:-2147483648 =&gt; 오버플로우

    }
}</code></pre>
<p>&lt;결과&gt;
<img src="https://velog.velcdn.com/images/baby_potato/post/96cd4fd0-a524-4839-884c-b4627a1b1e0b/image.png" alt="">
=&gt; maxIntOver는 int로 표현 할 수 있는 가장 큰 숫자보다 1이 큼 -&gt; <strong>오버플로우</strong> 발생 -&gt; intValue의 타입을 int에서 long 으로 변경</p>
<h2 id="계산과-형변환">계산과 형변환</h2>
<p>&lt;Casting4.java&gt;</p>
<pre><code class="language-java">package scope;

public class Casting4 {
    public static void main(String[] args) {
        int div1 = 3 / 2;
        System.out.println(&quot;div1 = &quot; + div1); //1

        double div2 = 3 / 2;
        System.out.println(&quot;div2 = &quot; + div2); //1.0

        double div3 = 3.0 / 2;
        System.out.println(&quot;div3 = &quot; + div3); //1.5

        double div4 = (double) 3 / 2;
        System.out.println(&quot;div4 = &quot; + div4); //1.5

        int a = 3;
        int b = 2;
        double result = (double) a / b;
        System.out.println(&quot;result = &quot; + result); //1.5
    }
}</code></pre>
<p>&lt;결과&gt;
<img src="https://velog.velcdn.com/images/baby_potato/post/07820020-e925-492e-b86c-c3063dc5ccee/image.png" alt=""></p>
<blockquote>
</blockquote>
<ol>
<li>같은 타입끼리의 계산은 같은 타입의 결과를 낸다.</li>
<li>서로 다른 타입의 계산은 큰 범위로 자동 형변환이 이루어진다.
int + long -&gt; long + long
int + double -&gt; double + double</li>
</ol>
<pre><code class="language-java">int div1 = 3 / 2; //int / int
int div1 = 1; //int / int이므로 int타입으로 결과가 나온다.</code></pre>
<pre><code class="language-java">double div2 = 3 / 2; //int / int
double div2 = 1; //int / int이므로 int타입으로 결과가 나온다.
double div2 = (double) 1; //int -&gt; double에 대입해야 한다. 자동 형변환 발생
double div2 = 1.0; // 1(int) -&gt; 1.0(double)로 형변환 되었다.</code></pre>
<pre><code class="language-java">double div3 = 3.0 / 2; //double / int
double div3 = 3.0 / (double) 2; //double / int이므로, double / double로 형변환이 발생한다.
double div3 = 3.0 / 2.0; //double / double -&gt; double이 된다.
double div3 = 1.5;</code></pre>
<pre><code class="language-java">double div4 = (double) 3 / 2; //명시적 형변환을 사용했다. (double) int / int
double div4 = (double) 3 / (double) 2; //double / int이므로, double / double로 형변환이 발생한다.
double div4 = 3.0 / 2.0; //double / double -&gt; double이 된다.
double div4 = 1.5;</code></pre>
<h2 id="정리">정리</h2>
<p>int -&gt; long -&gt; double</p>
<ul>
<li>작은 범위에서 큰 범위로 대입이 가능하다.
  이것을 묵시적 형변환 또는 자동 형변환이라 한다.</li>
<li>큰 범위에서 작은 범위의 대입은 문제가 발생할 수 있기 때문에 명시적 형변환을 사용해야한다.
  소수점버림, 오버플로우 등</li>
<li>연산과 형변환
  같은 타입은 같은 결과를 낸다.
  서로 다른 타입의 계산은 큰 범위로 자동 형변환이 일어난다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[JAVA 멍충이 탈출기 4]]></title>
            <link>https://velog.io/@baby_potato/JAVA-%EB%A9%8D%EC%B6%A9%EC%9D%B4-%ED%83%88%EC%B6%9C%EA%B8%B0-4</link>
            <guid>https://velog.io/@baby_potato/JAVA-%EB%A9%8D%EC%B6%A9%EC%9D%B4-%ED%83%88%EC%B6%9C%EA%B8%B0-4</guid>
            <pubDate>Wed, 03 Apr 2024 07:51:32 GMT</pubDate>
            <description><![CDATA[<h1 id="반복문">반복문</h1>
<p>특정 코드를 반복해서 실행할 때 사용
<strong>while , do-while , for</strong></p>
<h2 id="while문1">While문1</h2>
<blockquote>
<p>while (조건식) {
 // 코드
}</p>
</blockquote>
<ul>
<li>조건식을 확인한다. 참이면 코드 블럭을 실행하고, 거짓이면 while문을 벗어난다.</li>
<li>조건식이 참이면 코드 블럭을 실행한다. 이후에 코드 블럭이 끝나면 다시 조건식 검사로 돌아가서 조건식을 검사
한다.(무한 반복)</li>
</ul>
<p>&lt;while1_2.java&gt;</p>
<pre><code class="language-java">package loop;
public class While1_2 {
 public static void main(String[] args) {
 int count = 0;
 while (count &lt; 3) {
 count++;
 System.out.println(&quot;현재 숫자는:&quot; + count);
 }
 }
}</code></pre>
<h2 id="while문2">While문2</h2>
<p>&lt;While2_1.java&gt;</p>
<pre><code class="language-java">package loop;

public class While2_1 {
    public static void main(String[] args) {
        int sum =0;
        int i = 1;
        int endNum =3;

        while(i&lt;=endNum){
            sum = sum +i;
            System.out.println(&quot;i=&quot;+i+&quot;  sum=&quot;+sum);
            i++;
        }
    }
}</code></pre>
<p>=&gt; 코드가 실행되는 횟수를 유연하게 변경할 수 있어야 한다. 한마디로 같은 코드를 반복 실행할 수 있어야 한다.</p>
<h2 id="do-while문">do-while문</h2>
<p>while 문과 비슷하지만, 조건에 상관없이 무조건 한 번은 코드를 실행한다.</p>
<blockquote>
<p>do {
 // 코드
} while (조건식);</p>
</blockquote>
<p>&lt;DoWhile1.java&gt;</p>
<pre><code class="language-java">package loop;
public class DoWhile1 {
    public static void main(String[] args) {
        int i = 10;
        while (i &lt; 3) {
            System.out.println(&quot;현재 숫자는:&quot; + i);
            i++;
        }
    }
}</code></pre>
<p>=&gt; i=10 이기 때문에 아무것도 출력되지 않는다.</p>
<p>&lt;DoWhile2.java&gt;</p>
<pre><code class="language-java">package loop;
public class DoWhile2 {
    public static void main(String[] args) {
        int i = 10;
        do {
            System.out.println(&quot;현재 숫자는:&quot; + i);
            i++;
        } while (i &lt; 3);
    }
}</code></pre>
<p>=&gt; 현재숫자는: 10 한번만 실행되고 i&lt;3 조건에 맞지 않기 때문에 do-while 문을 빠져나오게 된다.</p>
<h2 id="break-continue">break, continue</h2>
<p><strong>break</strong>는 반복문을 종료하고 즉시 나간다. 
<strong>continue</strong>는 반복문의 나머지 부분을 건너뛰고 다음 반복을 진행하는데 사용한다.</p>
<blockquote>
<p>** break**
while(조건식) {
 코드1;
 break;//즉시 while문 종료로 이동한다.
 코드2;
}</p>
</blockquote>
<p>=&gt; 코드 2가 실행되지 않고 while문 종료된다.</p>
<blockquote>
<p><strong>continue</strong>
while(조건식) {
 코드1;
 continue;//즉시 조건식으로 이동한다.
 코드2;
}</p>
</blockquote>
<p>=&gt; 코드2가 실행되지 않고 다시 조건식으로 이동한다. 조건식이 참이면 while문이 실행된다.</p>
<p>&lt;Break1.java&gt;</p>
<pre><code class="language-java">package loop;

public class Break1 {
    public static void main(String[] args) {
        int sum = 0;
        int i = 1;
        while (true) { //조건이 항상 참이기 때문에 무한반복
            sum += i;
            if (sum &gt; 10) {
                System.out.println(&quot;합이 10보다 크면 종료: i=&quot; + i + &quot; sum=&quot; + sum);
                break; //빠져나오기
            }
            i++;
        }
    }
}</code></pre>
<p>&lt;결과&gt;
<img src="https://velog.velcdn.com/images/baby_potato/post/9ab08ab0-4131-4636-9a7d-35f0473f023b/image.png" alt=""></p>
<p>&lt;Continue1.java&gt;</p>
<pre><code class="language-java">package loop;

public class Continue1 {
    public static void main(String[] args) {
        int i = 1;
        while (i &lt;= 5) {
            if (i == 3) {
                i++;
                continue; // i를 하나 증가하고 continue, sout 실행하지 않고 바로 while문으로 이동
            }
            System.out.println(i);
            i++;
        }
    }
}</code></pre>
<p>&lt;결과&gt;
<img src="https://velog.velcdn.com/images/baby_potato/post/0dfb2e3e-1c23-4244-a8fa-293bd541963d/image.png" alt=""></p>
<h2 id="for문1">for문1</h2>
<p> for문은 주로 반복 횟수가 정해져 있을 때 사용한다.</p>
<blockquote>
<p>for (1.초기식; 2.조건식; 4.증감식) {
 // 3.코드
}</p>
</blockquote>
<blockquote>
<ol>
<li>초기식이 실행된다. 주로 반복 횟수와 관련된 변수를 선언하고 초기화 할 때 사용한다. 초기식은 딱 1번 사용된다.</li>
<li>조건식을 검증한다. 참이면 코드를 실행하고, 거짓이면 for문을 빠져나간다.</li>
<li>코드를 실행한다.</li>
<li>코드가 종료되면 증감식을 실행한다. 주로 초기식에 넣은 반복 횟수와 관련된 변수의 값을 증가할 때 사용한다.</li>
<li>다시 2. 조건식 부터 시작한다. (무한 반복)</li>
</ol>
</blockquote>
<p>&lt;For1.java&gt;</p>
<pre><code class="language-java">package loop;

public class For1 {
    public static void main(String[] args) {
        for (int i = 1; i &lt;= 10; i++) {
            System.out.println(i);
        }
    }
}</code></pre>
<p>&lt;For2.java&gt;</p>
<pre><code class="language-java">package loop;

public class For2 {
    public static void main(String[] args) {
        int sum =0;
        int endNum=3;

        for(int i=0; i&lt;=endNum; i++){
            sum = sum+i;
            System.out.println(&quot;sum : &quot;+ sum);
        }
    }
}</code></pre>
<h2 id="for문2">for문2</h2>
<blockquote>
<p>for (초기식; 조건식; 증감식) {
 // 코드
}
=&gt; for 문에서 초기식, 조건식, 증감식은 선택이고 생략이 가능하지만 세미콜론은 유지해야한다.</p>
</blockquote>
<blockquote>
<p>for (;;) {
 // 코드
}
=&gt; 무한반복
while (true) {
 // 코드
}
=&gt; 무한반복</p>
</blockquote>
<p>&lt;Break2.java&gt;</p>
<pre><code class="language-java">package loop;

public class Break2 {
    public static void main(String[] args) {
        int sum =0;

        for (int i=0; ;i++){
            sum +=i;
            if(sum&gt;10){
                System.out.println(&quot;10보다 큼 : &quot;+ i + &quot;  sum : &quot;+sum);
                break;
            }
        }
    }
}</code></pre>
<p>&lt;결과&gt;
<img src="https://velog.velcdn.com/images/baby_potato/post/1c03ee8d-cf23-4501-89fd-9410c560e6ba/image.png" alt=""></p>
<h2 id="중첩-반복문">중첩 반복문</h2>
<p>&lt;Nested1.java&gt;</p>
<pre><code class="language-java">package loop;

public class Nested1 {
    public static void main(String[] args) {
        for (int i = 0; i &lt; 2; i++) {
            System.out.println(&quot;외부 for 시작 i:&quot; + i);
            for (int j = 0; j &lt; 3; j++) {
                System.out.println(&quot;-&gt; 내부 for &quot; + i + &quot;-&quot; + j);
            }
            System.out.println(&quot;외부 for 종료 i:&quot; + i);
            System.out.println(); //라인 구분을 위해 실행
        }
    }
}</code></pre>
<p>&lt;결과&gt;
<img src="https://velog.velcdn.com/images/baby_potato/post/42e4fb1c-e74d-48f4-b489-138fa048b717/image.png" alt=""></p>
<h2 id="문제와-풀이1">문제와 풀이1</h2>
<p>&lt;WhileEx1.java&gt;</p>
<pre><code class="language-java">package loop.ex;

public class WhileEx1 {
    public static void main(String[] args) {
        //while
//        int count = 1;
//
//        while(count&lt;=10){
//            System.out.println(count);
//            count++;
//        }
        //for
        for(int i=1; i&lt;=10;i++){
            System.out.println(i);
        }
    }
}
</code></pre>
<p>&lt;WhileEx2.java&gt;</p>
<pre><code class="language-java">package loop.ex;

public class WhileEx2 {
    public static void main(String[] args) {
        //while
//        int num =1;

//        while (num&lt;=10){
//            System.out.println(num*2);
//            num++;
//        }
//
        //for
        for(int num=1; num&lt;=10; num++){
            System.out.println(num*2);
        }
    }
}</code></pre>
<p>&lt;WhileEx3.java&gt;</p>
<pre><code class="language-java">package loop.ex;

public class WhileEx3 {
    public static void main(String[] args) {
        //while
//        int max =5;
//        int sum =0;
//        int i =1;
//
//        while(i &lt;=max){
//            sum +=i;
//            System.out.println(&quot;i : &quot;+i+ &quot; sum : &quot;+sum);
//            i++;
//        }

        //for
        int max=5;
        int sum=0;
        for(int i=1;i&lt;=max;i++){
            sum +=i;
            System.out.println(&quot;i : &quot;+i+ &quot; sum : &quot;+sum);
        }
    }
}</code></pre>
<h2 id="문제와-풀이2">문제와 풀이2</h2>
<p>&lt;NestedEx1.java&gt;</p>
<pre><code class="language-java">package loop.ex;

public class NestedEx1 {
    public static void main(String[] args) {
        for(int i =1; i&lt;=9;i++){
            for(int j=1;j&lt;=9;j++){
                System.out.println(i+&quot;X&quot;+j+&quot;=&quot;+i*j);
            }
        }
    }
}</code></pre>
<p>&lt;NestedEx2.java&gt;</p>
<pre><code class="language-java">package loop.ex;

public class NestedEx2 {
    public static void main(String[] args) {
        int rows = 5;
        for(int i = 1; i &lt;= rows; i++) {
            for(int j = 1; j &lt;= i; j++) {
                System.out.print(&quot;*&quot;);
            }
            System.out.println();
        }
    }
}</code></pre>
]]></description>
        </item>
    </channel>
</rss>