<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>io-uty.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Tue, 11 Feb 2025 11:44:31 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>io-uty.log</title>
            <url>https://velog.velcdn.com/images/io-uty/profile/ba847051-3fa2-433a-afd6-b0835f505b3d/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. io-uty.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/io-uty" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[iOS/swift] 위치, 캘린더, 카메라 접근 권한 허용 및 고지 팝업 구현]]></title>
            <link>https://velog.io/@io-uty/iOSswift-%EC%9C%84%EC%B9%98-%EC%BA%98%EB%A6%B0%EB%8D%94-%EC%B9%B4%EB%A9%94%EB%9D%BC-%EC%A0%91%EA%B7%BC-%EA%B6%8C%ED%95%9C-%ED%97%88%EC%9A%A9-%EB%B0%8F-%EA%B3%A0%EC%A7%80-%ED%8C%9D%EC%97%85-%EA%B5%AC%ED%98%84</link>
            <guid>https://velog.io/@io-uty/iOSswift-%EC%9C%84%EC%B9%98-%EC%BA%98%EB%A6%B0%EB%8D%94-%EC%B9%B4%EB%A9%94%EB%9D%BC-%EC%A0%91%EA%B7%BC-%EA%B6%8C%ED%95%9C-%ED%97%88%EC%9A%A9-%EB%B0%8F-%EA%B3%A0%EC%A7%80-%ED%8C%9D%EC%97%85-%EA%B5%AC%ED%98%84</guid>
            <pubDate>Tue, 11 Feb 2025 11:44:31 GMT</pubDate>
            <description><![CDATA[<h3 id="위치-캘린더-카메라-접근-권한-허용-및-고지-팝업-구현">위치, 캘린더, 카메라 접근 권한 허용 및 고지 팝업 구현</h3>
<ul>
<li>스마트폰 앱 이용자에게 접근권한에 대한 내용을 고지하고 동의절차를 준수하도록 하기 위해</li>
</ul>
<ol>
<li><p><strong>접근권한 안내 고지 및 고지-설치 권한 일치 여부</strong></p>
</li>
<li><p><strong>접근권한 필수/선택 구분에 관련한 사항 고지</strong></p>
<p> → 위 두 사항을  충족하도록 구현함</p>
</li>
</ol>
<ul>
<li>필수 항목 - 위치, 카메라 권한 / 선택 항목 - 캘린더 접근 권한</li>
<li>위치 : 건물/길 찾기 (앱 첫 화면)</li>
<li>카메라 : Barcode scanner, QR scanner</li>
<li>캘린더 </li>
</ul>
<h3 id="위치-접근-권한-허용">위치 접근 권한 허용</h3>
<ul>
<li>앱 최초 실행 시 첫 화면에 접근 권한 허용 여부 묻기</li>
<li>허용 거부 시, 설정으로 이동하여 허용하도록 함</li>
<li>첫 화면에 구현하기 위해 MainViewController에 코드 작성</li>
</ul>
<pre><code class="language-swift">//위치 권한 접근 (필수적)
func requestLocationAccess() {
       locationManager.delegate = self
       locationManager.requestWhenInUseAuthorization()
}

func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
   switch status {
   case .authorizedWhenInUse, .authorizedAlways:
       print(&quot;위치 권한 허용됨&quot;)
   case .denied, .restricted:
       print(&quot;위치 권한 거부됨&quot;)
       self.presentPermissionAlert(title: &quot;위치 접근 권한 필요&quot;, message: &quot;앱이 위치를 확인하려면 권한이 필요합니다.&quot;)
   default:
       break
    }
 }</code></pre>
<h3 id="캘린더-접근-권한-허용">캘린더 접근 권한 허용</h3>
<ul>
<li>앱 최초 실행 시 첫 화면에 접근 권한 허용 여부 묻기</li>
<li>첫 화면에 구현하기 위해 MainViewController에 코드 작성</li>
</ul>
<pre><code class="language-swift">//캘린더 권한 접근 (선택적)
func requestCalendarAccess() {
    let eventStore = EKEventStore()

    eventStore.requestAccess(to: .event) { (granted, error) in
        DispatchQueue.main.async {
            if granted {
                print(&quot;캘린더 권한 허용됨&quot;)
            } else {
                print(&quot;캘린더 권한 거부됨&quot;)
            }
        }
    }
}</code></pre>
<h3 id="카메라-접근-권한-허용">카메라 접근 권한 허용</h3>
<ul>
<li>BarcodeScannerViewController에 코드 작성</li>
</ul>
<pre><code class="language-swift">// 카메라 권한 상태 확인 후 요청
AVCaptureDevice.requestAccess(for: .video) { response in
    if !response {
        DispatchQueue.main.async {
            self.presentPermissionAlert(
                title: &quot;카메라 권한 필요&quot;,
                message: &quot;카메라 사용 권한이 비활성화되어 있습니다. 설정에서 권한을 활성화하세요.&quot;
            )
        }
    }
}</code></pre>
<pre><code class="language-swift">// 카메라 권한 거부 시 설정으로 이동하는 알림
private func presentPermissionAlert(title: String, message: String) {
    let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
    alertController.addAction(UIAlertAction(title: &quot;설정으로 이동&quot;, style: .default, handler: { _ in
        guard let settingsURL = URL(string: UIApplication.openSettingsURLString) else { return }
    UIApplication.shared.open(settingsURL)
    }))
    alertController.addAction(UIAlertAction(title: &quot;취소&quot;, style: .cancel, handler: nil))
    present(alertController, animated: true, completion: nil)
}</code></pre>
<h3 id="접근-권한-필수선택-구분에-관련한-사항-고지"><strong>접근 권한 필수/선택 구분에 관련한 사항 고지</strong></h3>
<ul>
<li>앱 최초 실행 시 1회만 접근 권한 (필수/선택) 구분 사항 고지</li>
</ul>
<pre><code class="language-swift">//필수, 선택적 권한 고지
func showPermissionNotice() {
    // 최초 실행인지 확인
    if !UserDefaults.standard.bool(forKey: &quot;hasShownPermissionNotice&quot;) {

        DispatchQueue.global(qos: .background).async {
            DispatchQueue.main.async {
                let alertController = UIAlertController(
                    title: &quot;접근 권한 안내&quot;,
                        message:  &quot;&quot;&quot;
                       팝업에 원하는 문구 넣기
                        &quot;&quot;&quot;,
                    preferredStyle: .alert
                )

                alertController.addAction(UIAlertAction(title: &quot;종료&quot;, style: .default, handler: { _ in
                    self.requestLocationAccess()
                    self.requestCalendarAccess()
                }))

                alertController.addAction(UIAlertAction(title: &quot;확인&quot;, style: .default, handler: { _ in
                    self.requestLocationAccess()
                    self.requestCalendarAccess()
                    UserDefaults.standard.set(true, forKey: &quot;hasShownPermissionNotice&quot;)
                }))

                self.present(alertController, animated: true, completion: nil)
            }
        }
    }
}</code></pre>
<pre><code class="language-swift">//viewDidLoad 함수 내
        if isFirstLaunch() {
            showPermissionNotice()
        }</code></pre>
<h3 id="finally">Finally</h3>
<p>각 접근 권한 허용 팝업, 필수/선택적 허용 팝업 구현 후 어플 배포 완료</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[iOS/swift] barcode scanner zoom기능 구현]]></title>
            <link>https://velog.io/@io-uty/iOSswift-barcode-scanner-zoom%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84</link>
            <guid>https://velog.io/@io-uty/iOSswift-barcode-scanner-zoom%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84</guid>
            <pubDate>Tue, 11 Feb 2025 11:39:00 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h2 id="barcode-scanner-zoom기능-구현">barcode scanner zoom기능 구현</h2>
</blockquote>
<blockquote>
<h3 id="개요">개요</h3>
<p>관리하는 어플 내 barcode scanner 에서 아이폰 14 pro, 15 pro와 같이 카메라가 3개 이상인, 즉 광각모드를 지원하는 기기의 경우 바코드를 가까이 댔을 때 초점을 잃어 스캔하지 못하는 기기자체의 오류 발생 
   → 멀리서 초점은 맞춰지지만 바코드끼리 겹쳐져서 인식이 안되는 문제가 발생하는 것</p>
</blockquote>
<p>   그래서 든 생각은?</p>
<ul>
<li>가까이서 인식이 안되면 멀리서 인식해서 zoom in 하는 것은 어떨까? 였다!</li>
</ul>
<h4 id="가장-먼저-카메라-기본-권한설정-필수-info-필요권한주기">가장 먼저, 카메라 기본 권한설정 필수 (info-필요권한주기)</h4>
<ul>
<li>(key 지정 후 value 공란 비워두면 주의 메일 옴! 주의)</li>
</ul>
<p>그래서 내가 선택한 방법은,</p>
<blockquote>
<h4 id="1-광각카메라를-인식하여-광각카메라인-경우-카메라-입력에-추가하여-예외처리하기">1. 광각카메라를 인식하여 광각카메라인 경우 카메라 입력에 추가하여 예외처리하기</h4>
<p>   구현했지만 카메라를 제대로 인식하지 못해 보류 (코드엔 넣어놓음)</p>
</blockquote>
<pre><code>        // 광각 카메라 찾기
        guard let wideAngleCamera = AVCaptureDevice.DiscoverySession(
            deviceTypes: [.builtInWideAngleCamera],
            mediaType: .video,
            position: .back).devices.first else {
            fatalError(&quot;광각 카메라를 찾을 수 없습니다.&quot;)
        }
        captureDevice = wideAngleCamera
</code></pre><blockquote>
<h4 id="2-auto-focusing-구현">2. auto-focusing 구현</h4>
<p> 자동으로 초점을 맞추도록 구현하려고 했으나 어플에 해당 코드를 집어넣으면 코드에 오류 발생, 문제를 해결하지 못해 해당 방법은 기각함</p>
</blockquote>
<blockquote>
<h4 id="3-touch-focusing-구현">3. touch-focusing 구현</h4>
</blockquote>
<ul>
<li>터치하면 focusing 되도록 구현했고 작동은 하지만 문제를 해결할만큼 확실히 작동하는 것 같지는 않음</li>
</ul>
<h4 id="1-터치-포커스-구현-코드">1. 터치, 포커스 구현 코드</h4>
<pre><code>// 터치 제스처 설정
private func setupTapGestureRecognizer() {
    let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTapToFocus(_:)))
    view.addGestureRecognizer(tapGestureRecognizer)
}

@objc private func handleTapToFocus(_ tap: UITapGestureRecognizer) {
    let location = tap.location(in: view)
    let devicePoint = previewLayer.captureDevicePointConverted(fromLayerPoint: location)

    guard let device = captureDevice else { return }

    do {
        try device.lockForConfiguration()

        if device.isFocusPointOfInterestSupported {
            device.focusPointOfInterest = devicePoint
            device.focusMode = .autoFocus
        }

        if device.isExposurePointOfInterestSupported {
            device.exposurePointOfInterest = devicePoint
            device.exposureMode = .autoExpose
        }

        device.unlockForConfiguration()

        // 포커스 애니메이션
        focusView.center = location
        focusView.isHidden = false
        UIView.animate(withDuration: 0.3, animations: {
            self.focusView.transform = CGAffineTransform(scaleX: 1.25, y: 1.25)
        }) { _ in
            UIView.animate(withDuration: 0.3) {
                self.focusView.transform = CGAffineTransform.identity
            }
        }

    } catch {
        print(&quot;포커스 설정 중 오류 발생: \\(error.localizedDescription)&quot;)
    }
}

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    // 카메라 포커스 모드 설정
    if let videoCaptureDevice = captureDevice {
        do {
            try videoCaptureDevice.lockForConfiguration()
            if videoCaptureDevice.isFocusModeSupported(.continuousAutoFocus) {
                videoCaptureDevice.focusMode = .continuousAutoFocus
            }
            if videoCaptureDevice.isAutoFocusRangeRestrictionSupported {
                videoCaptureDevice.autoFocusRangeRestriction = .near
            }
            videoCaptureDevice.unlockForConfiguration()
        } catch {
            print(&quot;카메라 포커스 설정에 실패했습니다.&quot;)
        }
    }
}
</code></pre><h4 id="2-viewdidload-내에서-터치제스처-인식하는-함수-불러오기">2. viewDidLoad 내에서 터치제스처 인식하는 함수 불러오기</h4>
<pre><code>setupTapGestureRecognizer() // 터치 제스처 설정 추가</code></pre><h4 id="3-focus-animation">3. focus animation</h4>
<pre><code>// 포커스 뷰 선언
private var focusView: UIView!</code></pre><h4 id="4-viewdidload-내-설정">4. viewDidLoad 내 설정</h4>
<pre><code>// 포커스 애니메이션 뷰 설정
focusView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
focusView.layer.borderWidth = 1.0
focusView.layer.borderColor = UIColor.yellow.cgColor
focusView.backgroundColor = UIColor.clear
focusView.isHidden = true
view.addSubview(focusView)</code></pre><blockquote>
<h3 id="4-zoom-기능-구현">4. zoom 기능 구현</h3>
<p>차라리 멀리서 초점을 맞춰서 확대하자!</p>
</blockquote>
<h4 id="1-pinch-기능swift내부에-o-zoomfactor-사용">1. pinch 기능(swift내부에 o), zoomFactor 사용</h4>
<ul>
<li>핀치 제스처 설정</li>
</ul>
<pre><code>private func setupPinchGestureRecognizer() {
       let pinchGestureRecognizer = UIPinchGestureRecognizer(target: self, action: #selector(handlePinchCamera(_:)))
       view.addGestureRecognizer(pinchGestureRecognizer)</code></pre><ul>
<li>핀치 제스처 핸들러</li>
</ul>
<pre><code>@objc private func handlePinchCamera(_ pinch: UIPinchGestureRecognizer) {
       guard let device = captureDevice else { return }

       let minZoomFactor: CGFloat = 1.0
       let maxZoomFactor: CGFloat = device.maxAvailableVideoZoomFactor

       switch pinch.state {
       case .began:
           lastZoomFactor = device.videoZoomFactor
       case .changed:
           var newZoomFactor = lastZoomFactor * pinch.scale
           newZoomFactor = min(max(newZoomFactor, minZoomFactor), maxZoomFactor)
           do {
               try device.lockForConfiguration()
               device.videoZoomFactor = newZoomFactor
               device.unlockForConfiguration()
           } catch {
               print(&quot;줌 조절 중 오류 발생: \\(error.localizedDescription)&quot;)
           }
       case .ended, .cancelled:
           pinch.scale = 1.0
       default:
           break
       }
   }</code></pre><blockquote>
<h3 id="finally">Finally</h3>
<p>zoom 기능 구현 후 touch-focusing 보완해서 두 기능을 넣어 어플 배포 완료</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java/Spring] 연관관계 매핑 - Cascade, FetchType]]></title>
            <link>https://velog.io/@io-uty/JavaSpring-%EC%97%B0%EA%B4%80%EA%B4%80%EA%B3%84-%EB%A7%A4%ED%95%91-Cascade-FetchType</link>
            <guid>https://velog.io/@io-uty/JavaSpring-%EC%97%B0%EA%B4%80%EA%B4%80%EA%B3%84-%EB%A7%A4%ED%95%91-Cascade-FetchType</guid>
            <pubDate>Mon, 10 Feb 2025 08:07:10 GMT</pubDate>
            <description><![CDATA[<p>개발을 하다 보면 PK, FK, 영속성 전이, ManyToOne, OneToMany 등등 이런 단어와 어노테이션들을 자주 사용하게 되는데, 정확한 개념을 모르는 경우가 있다. 
앞으로 많이 사용하게 될테니 알아두자!</p>
<p>먼저 연관관계 매핑에 대해 알아보도록 하자.</p>
<blockquote>
<h2 id="연관관계-매핑이란">연관관계 매핑이란?</h2>
<p>연관관계 매핑이란, 데이터베이스에서 테이블 간의 관계를 정의하는 것이다.
즉, 객체(Entity) 간의 관계를 설정해 데이터를 효율적으로 저장하고 조회할 수 있도록 하는 과정이다.</p>
</blockquote>
<p>연관관계 매핑을 설명하기 전 기본 개념인 PK와 FK에 대해 설명하도록 하겠다.</p>
<h2 id="📌-pk기본-키와-fk외래-키">📌 PK(기본 키)와 FK(외래 키)</h2>
<blockquote>
<h3 id="pk-primary-key-기본-키">PK (Primary Key, 기본 키)</h3>
<p><strong>PK(기본 키)</strong>란 테이블에서 각 행(Row)을 유일하게 식별할 수 있는 값이다.
즉, 각 행을 구별하는 &quot;<strong>주인공</strong>&quot;이나 &quot;<strong>대표주자</strong>&quot; 역할을 한다고 생각하면 된다.</p>
</blockquote>
<h4 id="pk의-특징">PK의 특징</h4>
<ul>
<li>유일함(Unique)
: 한 테이블 내에서 같은 값이 중복될 수 없다.</li>
<li>NULL이 될 수 없음
: 반드시 값이 존재해야 함</li>
<li>한 테이블에 하나만 존재함 (복합키 제외)<h4 id="spring-boot-jpa에서-pk-설정">Spring Boot JPA에서 PK 설정</h4>
<pre><code>import jakarta.persistence.*;
</code></pre></li>
</ul>
<p>@Entity
public class User {
    @Id // PK 설정
    @GeneratedValue(strategy = GenerationType.IDENTITY) // 자동 증가 (Auto Increment)
    private Long id;</p>
<pre><code>private String name;
private String email;</code></pre><p>}</p>
<pre><code>- **@Id**를 사용하여 id 필드를 기본 키(PK)로 설정한다.
- **@GeneratedValue(strategy = GenerationType.IDENTITY)** 
 : 키 값이 자동 증가하도록 설정한다.


&gt;  ### FK (Foreign Key, 외래 키)
**FK(외래 키)**란 다른 테이블의 PK를 참조하는 키이다.
즉, 두 테이블을 연결해주는 &quot;**다리**&quot; 역할을 한다고 보면 된다.

#### FK의 특징
- 한 테이블이 다른 테이블을 참조할 때 사용한다.
- PK의 값을 가져와서 사용한다.
- 연관된 데이터를 무결성 있게 관리할 수 있다.

####  Spring Boot JPA에서 FK 설정</code></pre><p>import jakarta.persistence.*;</p>
<p>@Entity
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;</p>
<pre><code>@ManyToOne // 다대일 관계 설정
@JoinColumn(name = &quot;user_id&quot;) // FK 컬럼명 지정
private User user;

private String product;</code></pre><p>}</p>
<pre><code>- @JoinColumn(name = &quot;user_id&quot;)
: FK 컬럼명을 user_id로 지정하는 것이다.

**@ManyToOne**이라는 어노테이션이 바로 연관관계 매핑 중 하나이다.

&gt; ##  연관관계 매핑의 종류
#### 1:1 (일대일)    
- 하나의 엔티티가 다른 엔티티와 1:1로 연결하는 것이다
- 예) 사용자(User) - 프로필(Profile)
#### 1:N (일대다)    
- 하나의 엔티티가 여러 엔티티와 연결되는 것이다.
- 예) 사용자(User) - 주문(Order)
#### N:M (다대다)    
- 여러 엔티티가 여러 엔티티와 연결되는 것이다.
- 예) 주문(Order) - 장바구니(Cart)

## 📍 1:1 (일대일) 관계
#### : 한 엔티티가 다른 엔티티와 1개의 관계만 가짐

#### User.java</code></pre><p>@Entity
public class User {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;</p>
<pre><code>@OneToOne // 일대일 관계 설정
@JoinColumn(name = &quot;profile_id&quot;) // FK 설정
private Profile profile;</code></pre><p>}</p>
<pre><code>- User 엔티티는 id(PK), name, profile(FK)를 멤버로 가진다.
#### Profile.java</code></pre><p>@Entity
public class Profile {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String bio;
}</p>
<pre><code>- Profile 엔티티는 id(PK), bio를 멤버로 가진다.



## 📍 1:N (일대다) 관계
#### User.java</code></pre><p>@Entity
public class User {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;</p>
<pre><code>@OneToMany(mappedBy = &quot;user&quot;) // 일대다 관계 설정
private List&lt;Order&gt; orders = new ArrayList&lt;&gt;();</code></pre><p>}</p>
<pre><code>- User엔티티는 id(PK), name, orders를 멤버로 가진다.
#### Order.java</code></pre><p>@Entity
public class Order {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String product;</p>
<pre><code>@ManyToOne
@JoinColumn(name = &quot;user_id&quot;) // FK 설정
private User user;</code></pre><p>}</p>
<pre><code>- Order 엔티티는 id(PK), product, user(FK)를 멤버로 가진다.

여기서 FK는 **@ManyToOne**인 객체가 갖는 것이다.
**mappedBy**는 Order 테이블의 user 필드와 매핑한다는 뜻이다.

## 📍 N:M (다대다) 관계
다대다 관계에서는 중간 테이블을 생성하여 관리한다.
Student_Course라는 중간테이블에 id (PK), student_id(FK), course_id(FK) 이 세 멤버가 추가된다.
#### Student.java</code></pre><p>@Entity
public class Student {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;</p>
<pre><code>@ManyToMany
@JoinTable(
    name = &quot;student_course&quot;,
    joinColumns = @JoinColumn(name = &quot;student_id&quot;),
    inverseJoinColumns = @JoinColumn(name = &quot;course_id&quot;)
) // 중간 테이블 설정
private List&lt;Course&gt; courses = new ArrayList&lt;&gt;();</code></pre><p>}</p>
<pre><code>#### Course.java</code></pre><p>@Entity
public class Course {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String title;</p>
<pre><code>@ManyToMany(mappedBy = &quot;courses&quot;)
private List&lt;Student&gt; students = new ArrayList&lt;&gt;();</code></pre><p>}</p>
<pre><code>그러나 실무에서는 ManyToMany를 많이 사용하지 않는다.
추가적인 데이터 저장 불가능, 성능 문제, 데이터 변경 어려움 등의 이유라는데 일단 알아두기!

#### 다음으로, **영속성 전이**와 **조회 방식**에 대해 알아보자
&gt; ### Cascade (영속성 전이)
####  부모 엔티티의 변경이 자식 엔티티에도 영향을 미치도록 하는 설정</code></pre><p>@OneToMany(mappedBy = &quot;user&quot;, cascade = CascadeType.ALL)
private List<Order> orders = new ArrayList&lt;&gt;();</p>
<pre><code>
- **CascadeType.ALL **
: User가 삭제되면 Order도 삭제됨
- **CascadeType.PERSIST **
: User 저장 시 Order도 자동 저장
- **CascadeType.REMOVE **
: User 삭제 시 Order도 삭제

&gt; ### FetchType (지연 로딩 vs 즉시 로딩)
#### 연관된 엔티티를 조회할 때, 데이터를 즉시 가져올지(LAZY) 또는 필요할 때 가져올지(EAGER) 설정</code></pre><p>@ManyToOne(fetch = FetchType.LAZY)
private User user;</p>
<p>```</p>
<ul>
<li><strong>FetchType.LAZY</strong> 
: 필요할 때 데이터를 가져옴 (성능 최적화)</li>
<li>*<em>FetchType.EAGER *</em>
: 항상 데이터를 즉시 가져옴 (JOIN 실행됨, 성능 부담)</li>
</ul>
<h4 id="기본-설정">기본 설정</h4>
<p>@OneToOne, @ManyToOne → 기본값 EAGER
@OneToMany, @ManyToMany → 기본값 LAZY</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java/Spring] 스프링 MVC 패턴(2) : Controller]]></title>
            <link>https://velog.io/@io-uty/JavaSpring-%EC%8A%A4%ED%94%84%EB%A7%81-MVC-%ED%8C%A8%ED%84%B42-Controller</link>
            <guid>https://velog.io/@io-uty/JavaSpring-%EC%8A%A4%ED%94%84%EB%A7%81-MVC-%ED%8C%A8%ED%84%B42-Controller</guid>
            <pubDate>Mon, 10 Feb 2025 07:11:25 GMT</pubDate>
            <description><![CDATA[<p>지난 포스팅에 이어서, Controller에 대해 알아보도록 하자.</p>
<blockquote>
<h2 id="spring-mvc의-controller">Spring MVC의 Controller</h2>
</blockquote>
<ul>
<li>Spring MVC에서 <strong>Controller(컨트롤러)</strong>는 클라이언트의 요청을 받아 <strong>비즈니스 로직(Service)</strong>과 <strong>View</strong>(예를 들면, Thymeleaf)를 연결하는 역할을 한다.</li>
<li><strong>Controller</strong>는 <strong>사용자 요청을 처리</strong>하고, <strong>데이터를 Model에 담아 View로 전달</strong>하는 역할을 한다.</li>
</ul>
<blockquote>
<h2 id="controller의-역할">Controller의 역할</h2>
</blockquote>
<ul>
<li>클라이언트의 HTTP 요청을 처리한다.</li>
<li><strong>Service를 호출</strong>하여 비즈니스 로직을 수행한다.</li>
<li><strong>Model</strong>을 사용하여 <strong>View</strong>로 데이터 전달</li>
<li>REST API를 제공하는 경우 JSON 응답 반환 가능하다.</li>
</ul>
<p><strong>Controller는 클라이언트(View 또는 API 요청)와 Service 간의 다리 역할을 수행한다고 생각하면 된다.</strong></p>
<h3 id="📌-spring-mvc의-데이터-흐름-정리">📌 Spring MVC의 데이터 흐름 정리</h3>
<p><strong>클라이언트 → Controller → Service → Repository → Entity → DB → Entity → Repository → Service → Controller → Model → View(Thymeleaf) → 클라이언트 응답</strong></p>
<p>📍 참고로, 여기에 있는 Model은 <strong>MVC 패턴의 Model과 같은 것이 아니다</strong>.</p>
<ul>
<li>이 Model은 <strong>Spring MVC에서의 Model 객체 (org.springframework.ui.Model)</strong>인데,<br>Spring MVC에서 Model 객체는 <strong>Controller가 데이터를 View로 전달하는 역할</strong>을 한다.
즉, 여기서의 <strong>Model 객체</strong>는 <strong>단순히 View에서 사용할 데이터를 담는 컨테이너일 뿐</strong>이다!</li>
</ul>
<p>이제, 기본적인 예제를 살펴보도록 하자.</p>
<h3 id="1-controller-기본-예제">1. Controller 기본 예제</h3>
<pre><code>import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HomeController {

    @GetMapping(&quot;/&quot;)
    public String home(Model model) {
        model.addAttribute(&quot;message&quot;, &quot;Spring MVC 프로젝트에 오신 것을 환영합니다!&quot;);
        return &quot;home&quot;; // home.html로 이동
    }
}
</code></pre><ul>
<li>@Controller를 사용하여 Spring MVC 컨트롤러로 등록</li>
<li>@GetMapping(&quot;/&quot;) → 사용자가 / 경로로 GET 요청을 하면 home.html을 반환</li>
<li>Model을 사용하여 View에 데이터를 전달</li>
</ul>
<h3 id="2-controller에서-service-연동-예제">2. Controller에서 Service 연동 예제</h3>
<pre><code>import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.List;

@Controller
public class UserController {
    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping(&quot;/users&quot;)
    public String getUsers(Model model) {
        List&lt;UserDTO&gt; users = userService.getAllUsers();
        model.addAttribute(&quot;users&quot;, users);
        return &quot;user-list&quot;; // user-list.html로 이동
    }
}
</code></pre><ul>
<li>Service를 호출하여 데이터 가져오기</li>
<li>Model을 사용하여 View에 데이터를 전달</li>
</ul>
<h3 id="3-요청-파라미터-처리">3. 요청 파라미터 처리</h3>
<pre><code>import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class SearchController {

    @GetMapping(&quot;/search&quot;)
    public String search(@RequestParam String keyword, Model model) {
        model.addAttribute(&quot;keyword&quot;, keyword);
        return &quot;search-results&quot;;
    }
}
</code></pre><ul>
<li>view에서 이용 예시<pre><code>&lt;p&gt;검색어: &lt;span th:text=&quot;${keyword}&quot;&gt;&lt;/span&gt;&lt;/p&gt;
</code></pre></li>
</ul>
<pre><code>어노테이션이 굉장히 많은데, 어노테이션을 이해하면 Controller를 이해하기 더 쉬울 것이다.
다음으로 Controller에서 주로 사용하는 어노테이션에 대해 알아보자.
 ##  📍 Controller에서 사용하는 주요 어노테이션
&gt;  ### 1. @Controller vs @RestController
Controller를 정의할 때 가장 많이 사용하는 어노테이션이다.

### @Controller
-  Spring MVC 컨트롤러를 정의하는 어노테이션이다.
- 반환 타입은 **View(Thymeleaf, JSP)**이다.
- Thymeleaf와 같은 View 템플릿을 사용하여 **HTML을 반환**하는 경우 사용한다.</code></pre><p>@Controller
public class HomeController {
    @GetMapping(&quot;/&quot;)
    public String home(Model model) {
        model.addAttribute(&quot;message&quot;, &quot;Spring MVC 프로젝트!&quot;);
        return &quot;home&quot;; // home.html 템플릿 반환
    }
}</p>
<pre><code>### @RestController    
- REST API 컨트롤러를 정의하는 어노테이션이다.
- REST API를 개발할 때 사용하며, JSON 데이터를 반환할 때 사용한다.</code></pre><p>@RestController
@RequestMapping(&quot;/api&quot;)
public class ApiController {
    @GetMapping(&quot;/message&quot;)
    public String getMessage() {
        return &quot;Hello, this is a REST API response!&quot;;
    }
}</p>
<pre><code>#### 📍 만약 Controller에서 JSON을 반환하고싶다면, **@ResponseBody** 사용하기</code></pre><p>@Controller
@ResponseBody
public class ExampleController {
    @GetMapping(&quot;/json&quot;)
    public String getJson() {
        return &quot;{&quot;message&quot;: &quot;Hello, JSON!&quot;}&quot;;
    }
}</p>
<pre><code>- @ResponseBody(밑에서 설명)를 사용하면** @RestController와 동일한 효과**를 볼 수 있다.
- 참고로 **@RestController**는 **@Controller + @ResponseBody**의 조합이다.

&gt; ### 2. @RequestMapping vs @GetMapping vs @PostMapping
Spring에서 HTTP 요청을 처리하기 위해 사용되는 매핑 어노테이션이다.

### @RequestMapping    
: 공통 URL 패턴, 즉 모든 html 메서드(GET, POST, PUT 등)을 매핑하는 어노테이션이다.</code></pre><p>@Controller
@RequestMapping(&quot;/users&quot;)
public class UserController {
    @RequestMapping(&quot;/list&quot;)
    public String getUserList() {
        return &quot;user-list&quot;; // user-list.html 반환
    }
}</p>
<pre><code>### @GetMapping    
: HTTP GET 요청을 처리한다.
### @PostMapping
: HTTP POST 요청을 처리한다.</code></pre><p>@Controller
@RequestMapping(&quot;/users&quot;)
public class UserController {
    @GetMapping(&quot;/list&quot;)
    public String getUsers() {
        return &quot;user-list&quot;; // GET 요청
    }
    @PostMapping(&quot;/add&quot;)
    public String addUser() {
        return &quot;redirect:/users/list&quot;; // POST 요청
    }
}</p>
<pre><code>- @RequestMapping을 사용할 수도 있지만, @GetMapping 같은 세부적인 매핑을 권장한다고 한다!

&gt; ### 3. @RequestParam vs @PathVariable
클라이언트가 보낸 데이터를 받을 때 사용하는 어노테이션이다.

### @RequestParam    
- 쿼리 스트링 파라미터를 받는다.
- ?key=value 형태</code></pre><p>@Controller
@RequestMapping(&quot;/search&quot;)
public class SearchController {
    @GetMapping
    public String search(@RequestParam String keyword, Model model) {
        model.addAttribute(&quot;keyword&quot;, keyword);
        return &quot;search-results&quot;; // search-results.html 반환
    }
}</p>
<pre><code>- 클라이언트 요청은 http ://localhost:8080/search?keyword=Spring의 형태이며 @RequestParam을 통해 keyword 값을 가져온다.
### @PathVariable    
- URL 경로 변수를 받는다.
- /users/{id} 형태</code></pre><p>@RestController
@RequestMapping(&quot;/api/users&quot;)
public class UserRestController {
    @GetMapping(&quot;/{id}&quot;)
    public String getUserById(@PathVariable Long id) {
        return &quot;User ID: &quot; + id;
    }
}</p>
<pre><code>요청은 http: //localhost:8080/api/users/10의 형태이며 @PathVariable을 사용하여 id=10 값을 가져온다.

&gt; ### 4. @ModelAttribute vs @RequestBody
마지막으로, 사용자로부터 폼 데이터를 받을 때 사용하는 어노테이션이다.

### @ModelAttribute    
: 폼 데이터를 객체로 변환한다. (Form (HTML)형식)</code></pre><p>@Controller
@RequestMapping(&quot;/users&quot;)
public class UserController {</p>
<pre><code>@PostMapping(&quot;/form&quot;)
public String submitUserForm(@ModelAttribute UserDTO userDTO) {
    System.out.println(&quot;Name: &quot; + userDTO.getName());
    System.out.println(&quot;Email: &quot; + userDTO.getEmail());
    return &quot;redirect:/users&quot;;
}</code></pre><p>}</p>
<pre><code>- 사용예시</code></pre><form action="/users/form" method="post">
    <input type="text" name="name" placeholder="이름" />
    <input type="email" name="email" placeholder="이메일" />
    <button type="submit">제출</button>
</form>

<pre><code>&lt;form action=&quot;/users/form&quot; method=&quot;post&quot;&gt;
    &lt;input type=&quot;text&quot; name=&quot;name&quot; placeholder=&quot;이름&quot; /&gt;
    &lt;input type=&quot;email&quot; name=&quot;email&quot; placeholder=&quot;이메일&quot; /&gt;
    &lt;button type=&quot;submit&quot;&gt;제출&lt;/button&gt;
&lt;/form&gt;

### @RequestBody
: JSON 데이터를 객체로 변환한다.</code></pre><p>@RestController
@RequestMapping(&quot;/api/users&quot;)
public class UserRestController {</p>
<pre><code>@PostMapping
public String createUser(@RequestBody UserDTO userDTO) {
    return &quot;User created: &quot; + userDTO.getName();
}</code></pre><p>}</p>
<pre><code>
- 사용 예시</code></pre><p>{
    &quot;name&quot;: &quot;홍길동&quot;,
    &quot;email&quot;: &quot;<a href="mailto:hong@example.com">hong@example.com</a>&quot;
}</p>
<pre><code></code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[Java/Spring] 스프링 MVC 패턴(1) :  Model - Entity, Repository, Service, Dto]]></title>
            <link>https://velog.io/@io-uty/JavaSpring-MVC-%ED%8C%A8%ED%84%B41-Entity%EC%99%80-Repository%EB%9E%80</link>
            <guid>https://velog.io/@io-uty/JavaSpring-MVC-%ED%8C%A8%ED%84%B41-Entity%EC%99%80-Repository%EB%9E%80</guid>
            <pubDate>Mon, 10 Feb 2025 06:20:00 GMT</pubDate>
            <description><![CDATA[<p>보통 프로젝트를 할 때 MVC 패턴으로 개발을 하는데, 이는 역할을 분리하여 개발한다는 것이다.</p>
<p>이렇게 해야 유지보수가 쉽고 Controller, Model, View를 독립적으로 개발할 수 있어 확장성이 높기 때문인데, 일단 MVC가 무엇인지 알아야 효율적으로 개발을 할 수 있으니 MVC가 무엇인지 알아보도록 하자!</p>
<h2 id="📌-mvcmodel-view-controller-패턴이란">📌 MVC(Model-View-Controller) 패턴이란?</h2>
<blockquote>
<h4 id="소프트웨어-아키텍처-패턴-중-하나로-애플리케이션의-역할을-model-view-controller-3가지로-분리하여-유지보수와-확장성을-높이는-구조">소프트웨어 아키텍처 패턴 중 하나로, 애플리케이션의 역할을 Model-View-Controller 3가지로 분리하여 유지보수와 확장성을 높이는 구조</h4>
<p> Spring Boot에서 기본적으로 MVC패턴을 사용해서 웹 애플리케이션을 개발한다.</p>
</blockquote>
<blockquote>
<h3 id="spring-mvc의-동작-과정">Spring MVC의 동작 과정</h3>
</blockquote>
<ol>
<li>사용자가 요청(URL 요청)</li>
<li><strong>DispatcherServlet(프론트 컨트롤러)</strong>가 요청을 가로챔</li>
<li>요청을 적절한 <strong>Controller</strong>에게 전달</li>
<li>Controller는 요청을 처리하고, 필요한 데이터를** Service**를 통해 조회</li>
<li>Service는 데이터베이스와 연동되는 <strong>Repository를 호출</strong></li>
<li>Repository가 MySQL 등의 DB와 연결하여 데이터 조회 및 처리</li>
<li>조회한 데이터를 Model에 담아서 <strong>View(Thymeleaf)</strong>로 전달</li>
<li>Thymeleaf 템플릿 엔진이 데이터를 화면에 렌더링</li>
<li>완성된 HTML이 사용자에게 응답으로 반환됨</li>
</ol>
<p>아직 잘 이해가 되지 않을테니 밑에서 더 알아보도록 하자</p>
<h3 id="📍--mvc-패턴의-개념">📍  MVC 패턴의 개념</h3>
<blockquote>
<p>먼저 MVC란, 애플리케이션을 Model(모델) - View(뷰) - Controller(컨트롤러) 3가지 역할로 나누어 개발하는 구조를 말한다.</p>
</blockquote>
<p>가장 먼저 Model에 대해 알아보자.</p>
<blockquote>
<h2 id="model-모델">Model (모델)</h2>
</blockquote>
<ul>
<li>데이터를 처리하는 역할로, 데이터베이스와 연결되어 데이터를 저장하고 가공한다.</li>
<li>MVC 패턴에서 <strong>Model</strong>은 애플리케이션의 데이터를 다루는 모든 로직을 포함하는데, </li>
<li><em>Entity*</em>와 <strong>DTO</strong>, <strong>Service</strong>, <strong>Repository</strong>가 포함된다.</li>
</ul>
<h2 id="1-entity">1. Entity</h2>
<blockquote>
</blockquote>
<h3 id="개념">개념</h3>
<ul>
<li>데이터베이스의 테이블과 1:1 매핑되는 클래스이며 <strong>@Entity</strong> 어노테이션을 사용하여 정의한다.</li>
<li>데이터 저장, 조회, 수정, 삭제 등을 수행하는 객체 자체이며 일반적으로 <strong>Service</strong>나 <strong>Repository</strong>에서 사용된다.</li>
</ul>
<p>쉽게 말해 데이터베이스에 테이블, 즉 데이터가 있을 때, 우리는 작성한 클래스 위에** @Entity**라는 어노테이션을 붙여서_ 이 클래스가 엔티티이고 해당 테이블과 매핑된다!_ 라고 선언해주는 것이다.</p>
<h3 id="entity의-특징">Entity의 특징</h3>
<ul>
<li>DB 테이블과 매핑되어 직접적인 데이터 저장소 역할을 한다.</li>
<li>@Entity, @Table, @Id, @Column 등의 JPA 어노테이션을 사용한다.</li>
</ul>
<p>비즈니스 로직이 포함되지 않고, <strong>데이터 자체를 표현</strong>하는 것이 목적이다!</p>
<pre><code>import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@Entity
@Table(name = &quot;users&quot;)
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
    private String email;
    private String password;
}
</code></pre><p>예시 코드를 보면 <strong>@Entity</strong> 어노테이션으로 선언해준 것을 알 수 있다.</p>
<h2 id="2-repository">2. Repository</h2>
<blockquote>
<h3 id="개념-1">개념</h3>
</blockquote>
<ul>
<li>Entity를 이용해 데이터베이스와 직접 소통하는 계층이다.</li>
<li>데이터를 저장, 조회, 수정, 삭제(CRUD)하는 역할이며 Spring Data JPA를 사용하면 SQL을 직접 작성하지 않아도 자동으로 처리된다.</li>
</ul>
<h3 id="repository의-특징">Repository의 특징</h3>
<ul>
<li><strong>JpaRepository&lt;Entity, ID 타입&gt;</strong>을 상속하면 기본적인 *<em>CRUD 기능 *</em>제공</li>
<li><strong>DB와의 직접적인 연동</strong>을 담당하며, <strong>Entity</strong>를 다룬다.</li>
</ul>
<p>즉, Respository는 JPA를 사용하여 DB에 접근, 직접 상호작용하는 계층이며 <strong>Entity</strong>를 다루게 된다.</p>
<pre><code>import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository&lt;User, Long&gt; {
    // JpaRepository&lt;User, Long&gt;을 상속하면 기본 CRUD 메서드를 자동 생성
}
</code></pre><p>기본으로 제공되는 메서드는 다음과 같다.</p>
<blockquote>
<h3 id="📍-기본-제공-메서드">📍 기본 제공 메서드</h3>
</blockquote>
<h4 id="saveuser-user-----사용자-저장">save(User user)    : 사용자 저장</h4>
<h4 id="findbyidlong-id------특정-사용자-조회">findById(Long id)     : 특정 사용자 조회</h4>
<h4 id="findall-----모든-사용자-조회">findAll()    : 모든 사용자 조회</h4>
<h4 id="deletebyidlong-id------특정-사용자-삭제">deleteById(Long id)     : 특정 사용자 삭제</h4>
<h4 id="count-----총-사용자-수-반환">count()    : 총 사용자 수 반환</h4>
<h2 id="3-service란">3. Service란?</h2>
<blockquote>
<h3 id="개념-2">개념</h3>
</blockquote>
<ul>
<li><strong>비즈니스 로직을 처리</strong>하는 계층이다.</li>
<li><strong>Repository</strong>를 통해 데이터베이스에서 값을 조회하거나, 가공하여 <strong>Controller</strong>에 전달한다.</li>
<li>여러 <strong>Repository를 조합</strong>하여 복잡한 로직을 수행 가능</li>
</ul>
<h3 id="service의-역할">Service의 역할</h3>
<ul>
<li><strong>Controller에서 받은 요청</strong>을 처리하고 <strong>Repository를 호출</strong>하여 <strong>DB와 연동</strong>한다.</li>
<li>데이터를 가공하여 <strong>DTO</strong>로 변환 후 <strong>Controller</strong>에 반환한다.</li>
</ul>
<p>즉, Controller와 Repository의 중간 통로 역할을 한다고 생각하면 된다.</p>
<pre><code>import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class UserService {
    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    // 모든 사용자 조회 (Entity → DTO 변환)
    public List&lt;UserDTO&gt; getAllUsers() {
        return userRepository.findAll()
                .stream()
                .map(user -&gt; new UserDTO(user.getName(), user.getEmail()))
                .collect(Collectors.toList());
    }

    // 사용자 저장 (DTO → Entity 변환)
    public void saveUser(UserDTO userDTO) {
        User user = new User();
        user.setName(userDTO.getName());
        user.setEmail(userDTO.getEmail());
        user.setPassword(&quot;defaultPassword&quot;); // 실제 환경에서는 암호화 필요
        userRepository.save(user);
    }
}
</code></pre><ul>
<li><strong>getAllUsers()</strong> → <strong>Entity</strong>를 <strong>DTO</strong>로 변환 후 반환</li>
<li><strong>saveUser()</strong> → <strong>DTO</strong>를 <strong>Entity로 변환</strong> 후 저장</li>
<li><strong>Controller</strong>는 <strong>Service의 메서드를 호출하여 데이터를 처리</strong>한다.</li>
</ul>
<h2 id="4-dto란">4. DTO란?</h2>
<blockquote>
<h3 id="개념-3">개념</h3>
</blockquote>
<ul>
<li>계층 간 데이터 전송을 위해 사용하는 객체이다.</li>
<li>Entity와 달리 <strong>DB와 직접적인 연동이 없고</strong>, <strong>필요한 데이터만 포함</strong>한다.</li>
<li>Entity의 민감한 정보를 숨기거나, <strong>가공된 데이터를 전송</strong>하는 역할이다.</li>
</ul>
<h3 id="dto의-역할">DTO의 역할</h3>
<ul>
<li><strong>Controller ↔ Service ↔ Repository 간의 데이터 전송</strong></li>
<li>Entity의 불필요한 데이터를 숨기고, <strong>필요한 데이터만 포함</strong></li>
<li><strong>보안 강화</strong> (비밀번호 같은 민감한 정보를 제외 가능)</li>
<li>API 응답 시 DTO 사용으로 <strong>유연한 데이터 반환 가능</strong></li>
</ul>
<pre><code>import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@NoArgsConstructor
public class UserDTO {
    private String name;
    private String email;

    public UserDTO(String name, String email) {
        this.name = name;
        this.email = email;
    }
}
</code></pre><p>Entity 중 민감하지 않은 정보만 뽑아 정보를 전송하는 클래스라고 생각하면 된다.</p>
<ul>
<li>Entity와 다르게** password 필드가 없음**</li>
<li>View에서 사용할 데이터만 포함</li>
<li>Service에서 <strong>Entity → DTO 변환 후 반환</strong></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java/Spring] JPA란?]]></title>
            <link>https://velog.io/@io-uty/JavaSpring-JPA%EC%99%80-MVC</link>
            <guid>https://velog.io/@io-uty/JavaSpring-JPA%EC%99%80-MVC</guid>
            <pubDate>Sun, 09 Feb 2025 10:08:13 GMT</pubDate>
            <description><![CDATA[<p>백엔드 프로젝트를 본격적으로 시작하기 전 꼭 알아야 하는 JPA의 기본 개념에 대해 정리해보려고 한다.</p>
<blockquote>
<h3 id="📌-jpa-java-persistence-api란">📌 JPA (Java Persistence API)란?</h3>
</blockquote>
<h4 id="java-애플리케이션에서-데이터베이스와-상호작용하는-표준-인터페이스">Java 애플리케이션에서 데이터베이스와 상호작용하는 표준 인터페이스</h4>
<p>쉽게 말해, SQL 없이 객체만으로 데이터베이스를 조작할 수 있도록 도와주는 기술이다!</p>
<blockquote>
<h3 id=""></h3>
</blockquote>
<h4 id="java-객체를-데이터베이스-테이블과-매핑mapping하는-기술">Java 객체를 데이터베이스 테이블과 매핑(Mapping)하는 기술</h4>
<ul>
<li>SQL을 직접 작성하지 않아도 되기 때문에 객체만 다루면 된다.</li>
<li>Hibernate 같은 구현체가 JPA의 기능을 실제로 제공하며 자동으로 SQL을 생성하고 실행한다. 
즉 내 손으로 SQL문을 작성할 필요가 없다</li>
<li>데이터베이스가 바뀌어도 코드 수정이 거의 없다.</li>
</ul>
<blockquote>
<h3 id="📌-jpa-vs-jdbc">📌 JPA vs JDBC</h3>
<p>** SQL 직접 작성 여부 |    객체 매핑    | 학습 난이도 |    유지보수
** <strong>JDBC</strong>    - 직접 작성해야 함    | 없음    | 쉬움    | 어려움
<strong>JPA</strong> -     자동 생성 (필요 시 직접 작성 가능)    | 완전 지원 (ORM) |    어려움 |    쉬움
<strong>➡ JDBC는 SQL을 직접 작성해야 하지만, JPA는 SQL을 자동으로 생성하고 실행함!</strong></p>
</blockquote>
<p>이제 <strong>JPA의 동작 방식</strong>에 대해 알아보자.</p>
<h2 id="1-entity란">1. Entity란?</h2>
<blockquote>
<h3 id="jpa에서-데이터베이스의-테이블과-매핑되는-클래스이다"><strong>JPA에서 데이터베이스의 테이블과 매핑되는 클래스</strong>이다.</h3>
<p>이렇게 말하면 잘 감이 안올테니, 하나의 객체라고 생각하면 된다.
JPA에서는 database가 아닌 entity, 즉 객체 중심의 개발을 하는 것이다.
따라서 <em><strong>객체지향 프로그래밍</strong></em> 을 한다는 말이다!
객체지향 프로그래밍과 절차지향 프로그래밍에 대한 내용은 다음 포스팅에 ....</p>
</blockquote>
<pre><code>@Entity // JPA가 관리하는 엔티티임을 표시
@Table(name = &quot;member&quot;) // 테이블명 설정 (생략 가능)
public class Member {
    @Id // PK 설정
    @GeneratedValue(strategy = GenerationType.IDENTITY) // 자동 증가 (AUTO_INCREMENT)
    private Long id;
    @Column(nullable = false, length = 100) // DB 컬럼 설정
    private String name;
    private int age;
}</code></pre><h4 id="데이터베이스에-테이블을-만들고-이에-대응하는-클래스를-만들어-클래스-위에-entity-어노테이션-을-붙이면-이-클래스-자체가-엔티티가-되는-것이다">데이터베이스에 테이블을 만들고 이에 대응하는 클래스를 만들어 클래스 위에<em>** @Entity 어노테이션**</em> 을 붙이면 이 클래스 자체가 엔티티가 되는 것이다.</h4>
<ul>
<li><strong>@Entity</strong>: 해당 클래스를 JPA에서 관리하는 엔티티로 지정</li>
<li><strong>@Id</strong>: 기본 키(PK) 설정
  PK와 FK, 연관관계 매핑에 대해서는 추후 포스팅에서 다루겠다.</li>
<li><strong>@GeneratedValue(strategy = GenerationType.IDENTITY)</strong>: 자동 증가(AUTO_INCREMENT)</li>
<li>** @Column**: 컬럼 설정 (nullable, length 등)</li>
</ul>
<h2 id="2-엔티티-매니저--엔티티-매니저-팩토리">2. 엔티티 매니저 / 엔티티 매니저 팩토리</h2>
<blockquote>
<h3 id="엔티티-매니터-팩토리란">엔티티 매니터 팩토리란?</h3>
</blockquote>
<ul>
<li>엔티티 매니저 인스턴스를 관리하는 주체이다.
사용자로부터 요청이 오면 여기에서 앤티티 매니저를 생성한다.</li>
</ul>
<p>그렇다면, <strong>엔티티 매니저</strong>란 무엇일까?</p>
<blockquote>
<h3 id="엔티티-매니저란">엔티티 매니저란?</h3>
</blockquote>
<ul>
<li>엔티티 매니저란 내부적으로 영속성 컨텍스트에 접근해 엔티티에 대한 데이터베이스 작업을 제공하는 것을 말한다.</li>
</ul>
<p>다음은 <strong>영속성 컨텍스트</strong>에 대해 알아보자.</p>
<h2 id="3-영속성-컨텍스트란">3. 영속성 컨텍스트란?</h2>
<blockquote>
<h4 id="jpa가-엔티티를-관리하는-공간-즉-엔티티를-영구-저장하는-환경이다">JPA가 엔티티를 관리하는 공간, 즉 엔티티를 영구 저장하는 환경이다.</h4>
<p>엔티티를 데이터베이스에서 가져오거나 변경 사항을 감지하고 반영하는 역할을 한다.</p>
</blockquote>
<ul>
<li>persist() → 엔티티 저장</li>
<li>find() → 엔티티 조회 (1차 캐시 사용)</li>
<li>remove() → 엔티티 삭제</li>
<li>commit() → 변경 사항을 DB에 반영</li>
</ul>
<p>예시 코드를 보면, SQL을 직접 작성하지 않아도 persist()를 호출하면 자동으로 INSERT 문이 실행된다는 것을 볼 수 있다.</p>
<pre><code>EntityManager em = emf.createEntityManager();
em.getTransaction().begin(); // 트랜잭션 시작

Member member = new Member();
member.setName(&quot;홍길동&quot;);
member.setAge(25);

em.persist(member); // INSERT 자동 실행
em.getTransaction().commit(); // 트랜잭션 커밋
</code></pre><p>다음으로 spring data jpa에 대해 알아보도록 하자.</p>
<blockquote>
<h2 id="spring-data-jpa란">Spring Data JPA란?</h2>
</blockquote>
<h4 id="jpa를-더-쉽게-사용할-수-있도록-도와주는-라이브러리이다">JPA를 더 쉽게 사용할 수 있도록 도와주는 라이브러리이다</h4>
<p>기본적인 CRUD 기능을 자동으로 제공한다.</p>
<pre><code>public interface MemberRepository extends JpaRepository&lt;Member, Long&gt; {
    List&lt;Member&gt; findByName(String name); // 메서드 이름만으로 쿼리 자동 생성
}</code></pre><p>SQL을 직접 작성하지 않아도 findByName()을 호출하면 자동으로 SELECT * FROM member WHERE name = ? 실행된다.</p>
<blockquote>
<h2 id="📌-정리">📌 정리</h2>
</blockquote>
<ul>
<li>JPA는 SQL을 자동으로 생성하고 실행하는 기술</li>
<li>객체(Entity) 중심으로 개발할 수 있어 유지보수가 쉬움</li>
<li>Spring Data JPA를 활용하면 더 간단하게 CRUD 구현 가능</li>
</ul>
<p>JPA를 사용하면 SQL을 직접 작성하지 않고도 객체 지향적인 방식으로 데이터베이스를 조작할 수 있어 훨씬 편리하고 유지보수도 쉬워진다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java/Spring] intelliJ에서 Lombok 라이브러리 설치]]></title>
            <link>https://velog.io/@io-uty/JavaSpring-intelliJ%EC%97%90%EC%84%9C-Lombok-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-%EC%84%A4%EC%B9%98</link>
            <guid>https://velog.io/@io-uty/JavaSpring-intelliJ%EC%97%90%EC%84%9C-Lombok-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-%EC%84%A4%EC%B9%98</guid>
            <pubDate>Wed, 08 Jan 2025 04:57:02 GMT</pubDate>
            <description><![CDATA[<p>기본 설정이 끝났다면 (이전 포스팅 참고)</p>
<blockquote>
<h3 id="lombok-라이브러리">Lombok 라이브러리</h3>
<p>Getter/Setter, ToString과 같은 반복적인 자바 코드를 컴파일할 때 자동으로 생성해주는 라이브러리인 <strong>&quot;Lombok 라이브러리&quot;</strong>를 설치해주면 된다.</p>
</blockquote>
<h3 id="intellij---설정---플러그인에-들어가서-lombok을-검색해서-설치해준다">IntelliJ - 설정 - 플러그인에 들어가서 lombok을 검색해서 설치해준다.</h3>
<p><img src="https://velog.velcdn.com/images/io-uty/post/e622cd87-a9ce-49ca-9375-21a2530967da/image.png" alt=""></p>
<p>그리고 다시 설정으로 들어가서</p>
<h3 id="빌드실행배포---컴파일러---어노테이션-프로세서에-들어가서-어노테이션-처리-활성화에-체크되어있는지-확인한다-안되어있으면-체크하기">[빌드,실행,배포] - [컴파일러] - [어노테이션 프로세서]에 들어가서 &quot;어노테이션 처리 활성화&quot;에 체크되어있는지 확인한다. (안되어있으면 체크하기)</h3>
<p><img src="https://velog.velcdn.com/images/io-uty/post/12fe6e0e-8eee-4af0-84da-e2a0c66a805a/image.png" alt=""></p>
<h3 id="dependecy-추가">Dependecy 추가</h3>
<p>pom.xml에 </p>
<pre><code>        &lt;dependency&gt;
            &lt;groupId&gt;org.projectlombok&lt;/groupId&gt;
            &lt;artifactId&gt;lombok&lt;/artifactId&gt;
        &lt;/dependency&gt;</code></pre><p>이 코드를 추가해주면 끝!</p>
<ul>
<li>오류가 뜰 때 우클릭 -&gt; Maven -&gt; 프로젝트 다시 로드 하면 되는 경우도 있으니 참고!</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java/Spring] 어플리케이션 실행]]></title>
            <link>https://velog.io/@io-uty/JavaSpring-%EC%96%B4%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EC%8B%A4%ED%96%89</link>
            <guid>https://velog.io/@io-uty/JavaSpring-%EC%96%B4%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EC%8B%A4%ED%96%89</guid>
            <pubDate>Tue, 07 Jan 2025 06:32:42 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h3 id="spring-boot-project-생성">Spring Boot Project 생성</h3>
</blockquote>
<p><a href="https://start.spring.io/">https://start.spring.io/</a> 에 접속한 후 옵션들을 선택한다.</p>
<p><img src="https://velog.velcdn.com/images/io-uty/post/b1469525-b1fc-492a-8daf-cd9019c2e989/image.png" alt="">
자바 23버전이기 때문에 (jdk 설치하는 법은 이전 포스팅 참고!) Java 23으로 설정해주고
빌드 툴은 메이븐으로 설정한다.
또한 의존성 추가를 위해 우측 상단의 &#39;ADD DEPENDENCIES&#39;버튼을 눌러 &#39;Spring Web&#39;을 선택해준다.</p>
<ul>
<li>의존성을 추가한다는 것은 다른 라이브러리를 사용하기 위해 추가하는 것이라고 생각하면 됨!</li>
</ul>
<p>원하는 이름으로 설정해주고 &#39;GENERATE&#39; 버튼을 누르면 아티팩트 이름과 같은 파일이 다운로드 된다-!
  원하는 워크스페이스에서 압축을 풀어준다.
  <img src="https://velog.velcdn.com/images/io-uty/post/86f11d71-b141-4101-95d4-892b6620e88f/image.png" alt="">
  그리고 이전에 설치했던 인텔리제이 실행하여 &#39;열기&#39;를 눌러 압축 풀었던 파일을 열어준다.
  <img src="https://velog.velcdn.com/images/io-uty/post/553e0cbc-f5b3-425c-9118-d8cf15731139/image.png" alt="">
sdk까지 23버전으로 설정해주면 완료!</p>
<ul>
<li>main/java패키지 아래에 자바 소스코드를 작성</li>
<li>main/resources 디렉토리 아래 HTML, CSS, JS 등 정적 리소스 저장</li>
<li>템플릿 엔진인 thymeleaf는 기본적 뷰를 src/main/resources/templates/에서 찾는다.
해당 디렉토리 아래 HTML 파일을 작성하고 Controller Class 에서 반환한 뷰와 동일한 이름의 html 파일을 찾아 웹 드라우저에 띄운다.</li>
<li>src/test/java 패키지 아래 테스트 코드 작성</li>
</ul>
<blockquote>
<h3 id="maven">Maven</h3>
<p>Maven -  프로젝트의 빌드를 자동화해주는 빌트 둘이다. 개발 과정 중 많은 라이브러리가 필요한데 메이븐이 알아서 네트워크를 통해 다운로드하고 경로 지정까지 해준다!</p>
</blockquote>
<p>메이븐 설정에 들어가면
<img src="https://velog.velcdn.com/images/io-uty/post/678869c0-a8e1-4ecf-a94e-e34c03484b98/image.png" alt="">
이러한 창이 뜨고 로컬 저장소 확인이 가능하다.
여러 프로젝트를 동시 진행할 때 Local repository를 프로젝트별로 다른 폴더를 지정하는 것 추천!</p>
<blockquote>
<h3 id="설정파일">설정파일</h3>
<p>설정파일이란 스트링 부트 어플리케이션 실행 시 사용하는 여러가지 설정 값들을 정의하는 파일
src/main/resources 폴더 아래 자동생성된다.</p>
</blockquote>
<p>여기까지 하면 기본적인 설정 완료~</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java/Spring] macOS Java JDK23 설치하기]]></title>
            <link>https://velog.io/@io-uty/JavaSpring-macOS-Java-JDK23-%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@io-uty/JavaSpring-macOS-Java-JDK23-%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 07 Jan 2025 04:05:47 GMT</pubDate>
            <description><![CDATA[<p>Spring 공부를 시작하기에 앞서 가장 먼저 환경설정을 해주어야 한다.</p>
<blockquote>
<h3 id="jdk-설치">JDK 설치</h3>
<p>첫 번째 스텝은 JDK를 설치해주는 것!</p>
</blockquote>
<p>먼저 Oracle 사이트에서 Java를 다운받는다.
<a href="https://www.oracle.com/kr/java/technologies/downloads/#java23">https://www.oracle.com/kr/java/technologies/downloads/#java23</a>
<img src="https://velog.velcdn.com/images/io-uty/post/c174c486-7583-4ed9-bf98-1ffac3e95d49/image.png" alt="">
두 번째에 있는 ARM64 DMG Installer를 다운받으면 된다.
<img src="https://velog.velcdn.com/images/io-uty/post/824e31c7-72af-4918-baac-149d17475202/image.png" alt=""><img src="https://velog.velcdn.com/images/io-uty/post/2003bffa-6bae-44ba-a7f1-707d9510d78b/image.png" alt=""></p>
<p>계속 설치를 진행해주면 완료!</p>
<p>다음은 터미널에서 정상적으로 설치가 되었는지 확인하면 된다.</p>
<ul>
<li>command+space를 치면 검색창이 뜨는데 여기에서 터미널 검색 후에 열면 된다.</li>
</ul>
<ol>
<li>java --version (설치된 버전 확인)</li>
<li>which java (설치된 경로 확인)</li>
<li>cd /usr/bin (해당 경로로 이동)</li>
<li>ls -lrt <em>java</em> (java가 포함된 모든 파일 보기)</li>
</ol>
<p><img src="https://velog.velcdn.com/images/io-uty/post/043a0f77-4bce-4467-982d-2de8468d2174/image.png" alt=""></p>
<p>성공적으로 설치가 된 것을 볼 수 있다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[iOS] Apple Push Service 인증서 갱신]]></title>
            <link>https://velog.io/@io-uty/iOS-Apple-Push-Service-%EC%9D%B8%EC%A6%9D%EC%84%9C-%EA%B0%B1%EC%8B%A0%EB%B2%95</link>
            <guid>https://velog.io/@io-uty/iOS-Apple-Push-Service-%EC%9D%B8%EC%A6%9D%EC%84%9C-%EA%B0%B1%EC%8B%A0%EB%B2%95</guid>
            <pubDate>Mon, 16 Dec 2024 06:37:23 GMT</pubDate>
            <description><![CDATA[<ul>
<li>인증서는 1년에 한 번씩 계약 업데이트 하듯 만료 전에 갱신 해줘야 한다.</li>
</ul>
<blockquote>
<h2 id="1-csr-만들기">1. csr 만들기</h2>
<p> 키체인 접근 - 인증서 지원 - 인증 기관에서 인증서 요청... 
<img src="https://velog.velcdn.com/images/io-uty/post/98610643-1746-4e17-9912-ca4d6524aed7/image.png" alt=""> 
 이메일, 이름 작성 후 디스크에 저장됨 체크하고 계속 -&gt; <em><strong>csr 생성 완료!</strong></em>
<img src="https://velog.velcdn.com/images/io-uty/post/32919174-af19-4886-a2eb-f7c259871835/image.png" alt=""></p>
</blockquote>
<blockquote>
<h2 id="2-인증서-갱신">2. 인증서 갱신</h2>
</blockquote>
<h4 id="a-apple-developer-접속">a) Apple Developer 접속</h4>
<p><a href="https://developer.apple.com/">https://developer.apple.com/</a></p>
<h4 id="b-account---인증서---왼쪽-바-중-identifiers---인증서-갱신할-앱-선택">b) Account - 인증서 - 왼쪽 바 중 identifiers - 인증서 갱신할 앱 선택</h4>
<p><img src="https://velog.velcdn.com/images/io-uty/post/6c37f7d1-6ba1-4547-afe5-4ffc0c4a2c12/image.png" alt="">
<img src="https://velog.velcdn.com/images/io-uty/post/9b7fb593-1ca8-46bd-93cb-e10b80e0f921/image.png" alt=""></p>
<h4 id="c-push-notifications---edit--configure">c) Push Notifications - Edit / configure</h4>
<p><img src="https://velog.velcdn.com/images/io-uty/post/25c25762-d3a4-4e9d-9142-7e7caa6c8486/image.png" alt=""></p>
<h4 id="production-ssl-certificate---create-certificate">Production SSL Certificate - Create Certificate</h4>
<p><img src="https://velog.velcdn.com/images/io-uty/post/fa290608-9503-4c5f-bd7d-181d4dda5e4b/image.png" alt="">
아까 만들어두었던 csr 등록!</p>
<h4 id="d-인증서-파일을-다운로드--후-더블클릭">d) 인증서 파일을 다운로드  후 더블클릭</h4>
<p>-&gt; 키체인에 등록 완료</p>
<h4 id="e-키체인에-등록된-인증서-하위-항목에-있는-key-마우스-오른쪽-클릭---내보내기">e) 키체인에 등록된 인증서 하위 항목에 있는 key 마우스 오른쪽 클릭 - 내보내기</h4>
<p>-&gt; 인증서 생성 완료!</p>
<h2 id="3-인증서-교체">3. 인증서 교체</h2>
<ul>
<li>서버에서 새로운 인증서가 등록된 것 확인 후 만료된 인증서는 revoke
=&gt; 새로운 인증서로 교체하면 끝</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>