반응형
1. JSP의 정의와 역할을 이해한다.
2. JSP의 장점과 특징을 파악한다.
3. JSP와 관련된 기본 용어를 익힌다.

 

1. JSP란 뭘까?

Java Server Pages (JSP)는 동적인 웹 콘텐츠를 생성하기 위한 서버 측 기술이다. JSP는 HTML 페이지 내에 Java 코드를 포함시켜 웹 서버에서 실행된 후, 클라이언트에 HTML을 전송하는 방식으로 동작한다. 좀 더 자세히 말하자면 JSP(Java Server Pages)는 자바 서버 페이지의 약자로, 웹 애플리케이션 개발을 위한 서버 사이드 기술(Server-Side Rendering)입니다. JSP는 HTML, CSS, JavaScript 등의 클라이언트 사이드 언어와 함께 사용되어 동적인 웹 페이지를 생성하는 데 사용됩니다. 자바 코드를 HTML 코드에 삽입하여 웹 서버에서 실행할 수 있게 해주며, 그 결과는 클라이언트의 웹 브라우저로 전송됩니다.

 

SSR은 Server-Side Rendering(서버 측 렌더링)의 약어입니다. SSR은 클라이언트에게 웹 페이지를 제공하기 전에 서버에서 웹 페이지를 렌더링하여 HTML을 생성하는 웹 개발 방법론입니다. 이 방법론은 클라이언트에게 정적인 HTML을 먼저 제공하고, 클라이언트 측에서 JavaScript를 사용하여 동적인 콘텐츠를 추가하는 방식인 Client-Side Rendering(CSR)대비됩니다.

 

 

2. Servlet vs JSP

JSP 역할

웹 애플리케이션에서 사용자 요청을 처리하고, 동적으로 생성된 HTML을 클라이언트에게 반환한다. 프론트엔드와 백엔드의 중간 역할을 하며, HTML과 같은 프론트엔드 코드와 Java와 같은 백엔드 코드를 혼합하여 사용할 수 있다.

 

JSP의 기본 동작 원리

클라이언트 요청: 클라이언트(웹 브라우저)가 JSP 페이지에 접근한다.

서버 처리: 웹 서버(아파치 톰캣)가 JSP 페이지를 찾아 Java 서블릿으로 변환한 후 컴파일하여 실행한다.

응답 생성: JSP 페이지가 실행되어 동적인 HTML을 생성하고, 이를 클라이언트에게 전송한다.

 

샘플 코드 확인 1
<%@ page contentType="text/html; charset=UTF-8" language="java" %>
<html>
<head>
    <title>My First JSP Page</title>
</head>
<body>
    <h1>Hello, JSP!</h1>
    <%
        String message = "Welcome to Java Server Pages!";
        out.println("<p>" + message + "</p>");
    %>
</body>
</html>

 

샘플 코드 확인 2
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>JSP Example</title>
</head>
<body>
    <h1>JSP Example</h1>
    
    <hr>
    
    <%-- JSP 디렉티브 : 페이지 속성 설정 --%>
    <%@ page import="java.util.Date" %>
    <%@ page import="java.text.SimpleDateFormat" %>
    
    <%-- JSP 스크립트릿 : Java 코드 영역 --%>
    <%
        // 현재 날짜와 시간을 가져오기
        Date now = new Date();
        
        // 날짜 포맷 지정
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String formattedDate = sdf.format(now);
    %>
    
    <%-- HTML과 함께 JSP 표현식 사용하여 날짜 출력 --%>
    <p>현재 시간: <%= formattedDate %></p>
    
    <%-- JSP 선언문 : 변수 선언 --%>
    <%!
        int number = 10;
        String message = "Hello, JSP!";
    %>
    
    <%-- HTML과 함께 JSP 표현식 사용하여 변수 출력 --%>
    <p>숫자: <%= number %></p>
    <p>메시지: <%= message %></p>
    
    <%-- JSP 주석 : HTML 주석 --%>
    <!-- 이 부분은 HTML 주석입니다. JSP에서는 이렇게 사용할 수 있습니다. -->
    
    <%-- JSP 액션 태그 : Java 코드 실행 및 제어 --%>
    <%-- if-else 구문 --%>
    <% if (number > 5) { %>
        <p>숫자가 5보다 큽니다.</p>
    <% } else { %>
        <p>숫자가 5보다 작거나 같습니다.</p>
    <% } %>
    
     <%-- JSP 액션 태그 : include 지시자를 사용한 다른 JSP 파일 포함 --%>
    <%-- <%@ include file="included.jsp" %> -->
    
    
    <%-- JSP 표현언어(EL) : 변수 값 출력 --%>
    <p>EL 표현식을 사용한 숫자: ${10}</p>
    <p>EL 표현식을 사용한 메시지: ${"Hello, JSP!"}</p>
</body>
</html>

 

JSP 처리 과정

웹 브라우저에 JSP 페이지에 해당하는 URL을 입력하면, 톰캣 서버는 다음과 같은 과정을 거쳐 JSP 페이지를 실행한다.

 

JSP를 실행한다는 말은 곧 JSP 페이지를 컴파일한 결과인 서블릿 클래스를 실행한다는 의미가 된다.
반응형

'Java > Java 이론' 카테고리의 다른 글

[JSP] JSP 기초 문법  (0) 2024.07.03
[JSP] JSP 라이프사이클  (0) 2024.07.03
스레드(Thread)  (0) 2023.10.04
중첩 클래스  (0) 2023.10.02
람다 함수  (0) 2023.10.02
반응형

사이트 바로가기 : http://playhost.shop

 

이번 23년 11월 15일부터 12월 8일 대략 3주간 팀 프로젝트를 진행하였습니다.

 

팀원은 총 6명이 진행하였으며 팀당 2명씩 역할 분담을 나누어 사이트를 제작하였습니다.

 


📝 프로젝트 선정배경

프로젝트 선정배경은 프로젝트 주제 선정과정에서 강의 시간에 배웠던 API를 활용한 사이트을 제작하기로 회의 때 이야기를 하였고, 그 중 게임 소프트웨어 유통망 사이트인 Steam(스팀)의 API를 활용한 게임 판매 사이트를 제작하기로 결정하였습니다.

 


📝 프로젝트 목표

저희 playhost 사이트의 목표는 다음과 같습니다.

  • 직관적이고 한눈에 보기 쉬운 사이트 일 것
  • 스팀 API를 활용한 다양한 게임 조회 기능 구현
  • 사이트 관리자가 직접 판매할 게임을 추가/삭제할 수 있는 기능 구현
  • 포인트 구매 기능 구현

제작 기간은 23년 11월 15일 ~ 12월 08일 약 3주간 진행되었습니다.


📝 담당 역할

저는 사이트 프론트 + 백엔드 부분을 담당하였고 상세 기능은 아래와 같이 기능을 구현하였습니다.

  • 구매한 게임을 담을 수 있는 장바구니 기능 구현
  • 토스페이먼츠 API를 활용한 결제 시스템 구현
  • 게임 뉴스를 게시할 수 있는 News 페이지 기능 구현
  • 팀원 소개 및 사이트 소개 페이지 구현

📝 프로젝트에 사용한 기술

프로젝트에서 사용한 기술은 다음과 같습니다.

🔶 개발 툴

1) intellij IDEA - 백앤드 부분

2) VSCode - 프론트앤드 부분

3) Github - 형상관리

4) Oracle DB - DB

5) 배포 - 아마존 AWS


📝 시스템 구성도


📝 ERD Table

 


📝 로그인 기능

 

먼저 사이트 로그인 기능입니다.

로그인은 이메일, 비밀번호를 입력하여 로그인을 할 수 있습니다.

회원가입의 경우 이름, 이메일, 비밀번호, 비밀번호 재입력을 통해 회원가입을 합니다.

 

회원가입을 진행하면 회원가입시 입력한 이메일로 인증번호가 전송되며 전송된 인증번호를 입력하여 회원가입을 진행합니다.

 

비밀번호 찾기 기능은 가입시 입력한 이메일을 입력하면 이메일 주소로 인증번호가 전송됩니다.

그 인증번호를 입력하여 일치하면 비밀번호 변경 부분으로 넘어가게 되고, 새로운 비밀번호로 변경이 가능합니다.

 

소셜로그인 부분입니다.

 

소셜로그인은 구글, 카카오로 로그인이 가능하며 별도 회원가입 없이 구글과 카카오에 로그인이 되어있으면 다이렉트로 로그인이 진행됩니다.

 


📝 메인페이지

메인페이지는 사이트 처음 접속시 보이는 페이지입니다.

  • 추천게임 목록 부분은 관리자 페이지에서 해당 상품에 이미지를 업로드 한 경우 메인 화면에 표시가 됩니다.
  • 금주의 할인 게임은 관리자 페이지에서 판매 제품에 할인율을 적용할 경우 할인 게임 탭 부분에 표시가 됩니다.


📝 상세 조회

상세 조회 페이지부분은 게임 태그별(액션, 어드벤처 등...) 게임 목록들을 보여주는 페이지입니다.

쇼핑몰로 치면 카테고리별로 상품을 보여주는 부분이라 생각하시면 됩니다.


📝 게임 상세조회

게임 목록에서 해당 게임을 선택하면 보이는 페이지 입니다.

화면 상단에는 썸네일 이미지 + 타이틀이름 + 리뷰 상태를 표시하며

화면 중간 부분에는 게임 스크린샷, 게임 트레일러 영상 등이 표시됩니다.

스크롤을 내리면 게임 설명부분과 사이드바 부분으로 나누어져 있으며 장바구니 추가 버튼이 존재합니다.

 

해당 게임에 대한 리뷰도 작성이 가능합니다. 단, 게임을 구매한 경우에만 리뷰 작성이 가능하도록 설정되어 있습니다.


📝 장바구니 기능

 

장바구니 부분은 게임 상세조회에서 장바구니 탭 부분에 금액이 적힌 버튼을 누르게 되면 장바구니에 추가가 됩니다.

 

장바구니 페이지로 넘어가게 되면 해당 장바구니에 담긴 개수 표시와 장바구니에 담긴 제품 상세 표시, 주문하기 버튼으로 구성되어 있습니다.

 


📝 결제 페이지

위의 장바구니 페이지에서 결제하기 버튼을 누르면 결제 페이지로 넘어가게 됩니다.

 

결제 페이지부분은

유저의 주문내역 부분과 포인트 영역, 토스페이먼츠 API를 이용한 결제 선택창이 표시됩니다.

토스페이먼츠 API를 이용하여 실제 결제는 되지 않고 테스트 결제가 되어 결제가 진행됩니다.

 

구매완료시 해당 계정 이메일로 구매내역을 전송합니다.

 

 

 

다음시간에는 그 외 기능을 포스팅하겠습니다~🙋‍♂️

반응형
반응형

💡 SQL을 추상화한 JPQL

JPA : ORM(Object-Relational Mapping) 프레임워크

JPQL(Java Persistence Query Language)

JPA는 SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어를 제공합니다.

따라서 테이블을 대상으로 쿼리 하는 것이 아닌 엔티티 객체를 대상으로 쿼리합니다.

JPQL은 SQL을 추상화했기 때문에 특정 데이터베이스 SQL에 의존하지 않는 장점이 있습니다.

JPQL은 SQL과 문법이 유사하며, SELECT, FROM, WHERE, GROUP BY, HAVING, JOIN을 지원합니다.


💡 쿼리메소드를 사용한 쿼리문 만들기

✅ TODO 1) 전체 조회 + 정렬(내림차순)

📂 DeptRepository.java

package com.example.jpacustomexam.repository;

import com.example.jpacustomexam.model.Dept;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
 * packageName : com.example.jpacustomexam.repository
 * fileName : DeptRepository
 * author : GGG
 * date : 2023-10-17
 * description : 부서 레포지토리 (기본 CRUD 함수)
 * 요약 :
 * <p>
 * ===========================================================
 * DATE            AUTHOR             NOTE
 * —————————————————————————————
 * 2023-10-17         GGG          최초 생성
 */
@Repository
public interface DeptRepository extends JpaRepository<Dept, Integer> {
    // 개발자가 직접 SQL 작성 하는 기능(JPQL) : 1) 쿼리메소드
    //                                      2) @Query 쓰는 방법
    // todo: 1) 전체조회 + 정렬(내림차순)
    List<Dept> findAllByOrderByDnoDesc();
}

📂 DeptService.java

package com.example.jpacustomexam.service;

import com.example.jpacustomexam.model.Dept;
import com.example.jpacustomexam.repository.DeptRepository;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * packageName : com.example.jpacustomexam.service
 * fileName : DeptService
 * author : GGG
 * date : 2023-10-17
 * description : 부서 서비스
 * 요약 :
 * <p>
 * ===========================================================
 * DATE            AUTHOR             NOTE
 * —————————————————————————————
 * 2023-10-17         GGG          최초 생성
 */
@Service
public class DeptService {
    @Autowired
    DeptRepository deptRepository;

    /** 전체조회 + 정렬(dno 내림차순) */
    // todo) 쿼리메소드를 사용한 함수
    public List<Dept> findAllByOrderByDnoDesc(){
        List<Dept> list = deptRepository.findAllByOrderByDnoDesc();
        return list;
    }
}

 

📂 DeptController.java

package com.example.jpacustomexam.controller;

import com.example.jpacustomexam.model.Dept;
import com.example.jpacustomexam.service.DeptService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
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;

/**
 * packageName : com.example.jpacustomexam.controller
 * fileName : DeptController
 * author : GGG
 * date : 2023-10-17
 * description : 부서 컨트롤러 (리액트 용)
 * 요약 :
 * <p>
 * ===========================================================
 * DATE            AUTHOR             NOTE
 * —————————————————————————————
 * 2023-10-17         GGG          최초 생성
 */
@Slf4j
@RestController
public class DeptController {
    @Autowired
    DeptService deptService;    // DI

    /** 전체 조회 + 정렬(dno 내림차순) */
    @GetMapping("/dept/desc")
    public ResponseEntity<Object> getDeptAllDesc(){
        try {
            // todo : 전체 조회 + 정렬(dno 내림차순)함수 호출
            List<Dept> list = deptService.findAllByOrderByDnoDesc();
            if (list.isEmpty() == false) {
//                성공
                return new ResponseEntity<>(list, HttpStatus.OK);
            } else {
//                데이터 없음
                return new ResponseEntity<>(HttpStatus.NO_CONTENT);
            }
        } catch (Exception e) {
            log.debug(e.getMessage());
            return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
}

 

✔ 서버 재시작 후 API 테스트 

부서번호가 내림차순으로 정렬된 것을 알 수 있습니다.

1) 쿼리메소드 : 함수이름으로 sql문 생성

예) findAllByOrderByDnoDesc

findAllByOrderByDnoDesc
findAll 전체조회
OrderBy 정렬
Dno 대상컬럼명
Desc 내림차순

 

💡 JPA의 쿼리메소드 샘플은 다음과 같습니다.

출처:https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods         

Distinct findDistinctByLastnameAndFirstname select distinct …​ where x.lastname = ?1 and x.firstname = ?2
And findByLastnameAndFirstname … where x.lastname = ?1 and x.firstname = ?2
Or findByLastnameOrFirstname … where x.lastname = ?1 or x.firstname = ?2
Is, Equals findByFirstname,findByFirstnameIs,findByFirstnameEquals … where x.firstname = ?1
Between findByStartDateBetween … where x.startDate between ?1 and ?2
LessThan findByAgeLessThan … where x.age < ?1
LessThanEqual findByAgeLessThanEqual … where x.age <= ?1
GreaterThan findByAgeGreaterThan … where x.age > ?1
GreaterThanEqual findByAgeGreaterThanEqual … where x.age >= ?1
After findByStartDateAfter … where x.startDate > ?1
Before findByStartDateBefore … where x.startDate < ?1
IsNull, Null findByAge(Is)Null … where x.age is null
IsNotNull, NotNull findByAge(Is)NotNull … where x.age not null
Like findByFirstnameLike … where x.firstname like ?1
NotLike findByFirstnameNotLike … where x.firstname not like ?1
StartingWith findByFirstnameStartingWith … where x.firstname like ?1 (parameter bound with appended %)
EndingWith findByFirstnameEndingWith … where x.firstname like ?1 (parameter bound with prepended %)
Containing findByFirstnameContaining … where x.firstname like ?1 (parameter bound wrapped in %)
OrderBy findByAgeOrderByLastnameDesc … where x.age = ?1 order by x.lastname desc
Not findByLastnameNot … where x.lastname <> ?1
In findByAgeIn(Collection<Age> ages) … where x.age in ?1
NotIn findByAgeNotIn(Collection<Age> ages) … where x.age not in ?1
True findByActiveTrue() … where x.active = true
False findByActiveFalse() … where x.active = false
IgnoreCase findByFirstnameIgnoreCase … where UPPER(x.firstname) = UPPER(?1)

✅ TODO 2) 전체 조회 + 정렬(내림차순) - 부서명으로 내림차순, 부서번호 오름차순

📂 DeptRepository.java 추가

    // todo: 2) 전체조회 + 정렬(부서명 내림차순)
    List<Dept> findAllByOrderByDnameDesc();

    // todo: 3) 전체조회 + 정렬(부서번호 오름차순) : Asc 생략가능
    List<Dept> findAllByOrderByDno();
}

📂 DeptService.java 추가

/** 전체조회 + 정렬(dname 내림차순) : 쿼리메소드 */
public List<Dept> findAllByOrderByDnameDesc(){
    List<Dept> list = deptRepository.findAllByOrderByDnameDesc();
    return list;
}

/** 전체조회 + 정렬(dno 오름차순) : 쿼리메소드 */
public List<Dept> findAllByOrderByDno(){
    List<Dept> list = deptRepository.findAllByOrderByDno();
    return list;
}

📂 DeptController.java 추가

 /** 전체 조회 + 정렬(dname 내림차순) */
    @GetMapping("/dept/dname/desc")
    public ResponseEntity<Object> findAllByOrderByDnameDesc(){
        try {
            // todo : 전체 조회 + 정렬(dno 내림차순)함수 호출
            List<Dept> list = deptService.findAllByOrderByDnameDesc();
            if (list.isEmpty() == false) {
//                성공
                return new ResponseEntity<>(list, HttpStatus.OK);
            } else {
//                데이터 없음
                return new ResponseEntity<>(HttpStatus.NO_CONTENT);
            }
        } catch (Exception e) {
            log.debug(e.getMessage());
            return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
    /** 전체 조회 + 정렬(dno 오름차순) */
    @GetMapping("/dept/dno/asc")
    public ResponseEntity<Object> findAllByOrderByDno(){
        try {
            // todo : 전체 조회 + 정렬(dno 내림차순)함수 호출
            List<Dept> list = deptService.findAllByOrderByDno();
            if (list.isEmpty() == false) {
//                성공
                return new ResponseEntity<>(list, HttpStatus.OK);
            } else {
//                데이터 없음
                return new ResponseEntity<>(HttpStatus.NO_CONTENT);
            }
        } catch (Exception e) {
            log.debug(e.getMessage());
            return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

✔ 서버 재시작 후 API 테스트 

1) 전체조회 - 부서이름으로 내림차순

2) 전체조회 - 부서번호 오름차순

✅ TODO 3) 전체조회 + dname(부서명) like 검색 + dname 내림차순 조회

📂 DeptRepository.java 추가

// todo: 4) dname like 검색(DnameContaining)+ dname 내림차순 조회
List<Dept> findAllByDnameContainingOrderByDnameDesc(String dname);

📂 DeptService.java 추가

/** dname(부서명) like 검색 + dname 내림차순 조회 */
public List<Dept> findAllByDnameContainingOrderByDnameDesc(String dname){
    List<Dept> list 
            = deptRepository.findAllByDnameContainingOrderByDnameDesc(dname);
    return list;
}

📂 DeptController.java 추가

 /** 전체조회 + dname(부서명) like 검색 + dname 내림차순 조회 */
    @GetMapping("/dept/dname/containing/desc/{dname}")
    public ResponseEntity<Object> findAllByDnameContainingOrderByDnameDesc(
            @PathVariable String dname
    ){
        try {
            // todo : 전체 조회 + 정렬(dno 내림차순)함수 호출
            List<Dept> list
                    = deptService
                    .findAllByDnameContainingOrderByDnameDesc(dname);
            if (list.isEmpty() == false) {
//                성공
                return new ResponseEntity<>(list, HttpStatus.OK);
            } else {
//                데이터 없음
                return new ResponseEntity<>(HttpStatus.NO_CONTENT);
            }
        } catch (Exception e) {
            log.debug(e.getMessage());
            return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

✔ 서버 재시작 후 API 테스트 

1) 전체조회 - 부서명 like 검색 + dname 내림차순 조회

✅ TODO 4) 쿼리메소드 응용 연습문제

// todo) 응용연습
//    todo: 응용 연습문제 
//    todo: 연습 4) EMP 테이블에서 Job 이 manager 이고
//              매개변수로 부서번호(dno)를 받는 함수를 작성하세요.

//    todo: 연습 5) Emp 테이블에서 salary 가 1000 ~ 1500 사이의 값을 같는
//     사원을 조회하려고 합니다.  함수를 작성해 주세요
더보기

📂 EmpRepository.java

 

List<Emp> findAllByJobAndDno(String job, int dno);

List<Emp> findAllBySalaryBetween(int first, int last);

📂 EmpService.java

//    todo: 연습 4) EMP 테이블에서 Job 이 manager 이고
//              매개변수로 부서번호(dno)를 받는 함수를 작성하세요.
public List<Emp> findAllByJobAndDno(String job, int dno){
    List<Emp> list = empRepository.findAllByJobAndDno(job, dno);
    return list;
}

//    todo: 연습 5) Emp 테이블에서 salary 가 1000 ~ 1500 사이의 값을 같는
//     사원을 조회하려고 합니다.  함수를 작성해 주세요
public List<Emp> findAllBySalaryBetween(int first, int last){
    List<Emp> list = empRepository.findAllBySalaryBetween(first, last);
    return list;
}

📂 EmpController.java

  /** 연습 4) : 쿼리메소드 */
    @GetMapping("/emp/dno/{dno}")
    public ResponseEntity<Object> findAllByEnameContaining(
            @PathVariable int dno
    ) {
        try {
//         전체 조회 + 정렬(dno 내림차순) 호출
            List<Emp> list
                    = empService.findAllByJobAndDno("MANAGER", dno);

            if (list.isEmpty() == false) {
//                성공
                return new ResponseEntity<>(list, HttpStatus.OK);
            } else {
//                데이터 없음
                return new ResponseEntity<>(HttpStatus.NO_CONTENT);
            }

        } catch (Exception e){
            log.debug(e.getMessage());
            return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    /** 연습5 */

    @GetMapping("emp/salary/{first}/{last}")
    public ResponseEntity<Object> findAllBySalaryBetween(@PathVariable int first, @PathVariable int last){
        try {
            List<Emp> list
                    = empService.findAllBySalaryBetween(first, last);
            if(list.isEmpty() == false){
                return new ResponseEntity<>(list, HttpStatus.OK);
            } else {
                return new ResponseEntity<>(HttpStatus.NO_CONTENT);
            }

        } catch (Exception e) {
            log.debug(e.getMessage());
            return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

✔ 서버 재시작 후 API 테스트 

연습 4)

 

연습 5)

[
  {
    "insertTime": "2023-10-17 06:38:45",
    "updateTime": null,
    "eno": 7521,
    "ename": "WARD",
    "job": "SALESMAN",
    "manager": 7698,
    "hiredate": "1981-02-22 00:00:00",
    "salary": 1250,
    "commission": 500,
    "dno": 30
  },
  {
    "insertTime": "2023-10-17 06:38:45",
    "updateTime": null,
    "eno": 7654,
    "ename": "MARTIN",
    "job": "SALESMAN",
    "manager": 7698,
    "hiredate": "1981-09-28 00:00:00",
    "salary": 1250,
    "commission": 1400,
    "dno": 30
  },
  {
    "insertTime": "2023-10-17 06:38:45",
    "updateTime": null,
    "eno": 7844,
    "ename": "TURNER",
    "job": "SALESMAN",
    "manager": 7698,
    "hiredate": "1981-09-08 00:00:00",
    "salary": 1500,
    "commission": 0,
    "dno": 30
  },
  {
    "insertTime": "2023-10-17 06:38:45",
    "updateTime": null,
    "eno": 7876,
    "ename": "ADAMS",
    "job": "CLERK",
    "manager": 7788,
    "hiredate": "1987-07-13 00:00:00",
    "salary": 1100,
    "commission": null,
    "dno": 20
  },
  {
    "insertTime": "2023-10-17 06:38:45",
    "updateTime": null,
    "eno": 7934,
    "ename": "MILLER",
    "job": "CLERK",
    "manager": 7782,
    "hiredate": "1982-01-23 00:00:00",
    "salary": 1300,
    "commission": null,
    "dno": 10
  }
]
응답 파일이 저장되었습니다.
> 2023-10-17T153953.200.json

Response code: 200; Time: 25ms (25 ms); Content length: 933 bytes (933 B)

💡 @Query 이용한 쿼리문 작성

데이터베이스에서 값을 가져올 때는 위의 코드처럼 메서드의 이름만으로 쿼리 메서드를 생성할 수 도 있습니다.

이번에는 @Query 어노테이션을 사용해 직접 JPQL을 작성해봅시다.

 

📂 DeptRepository.java

    //    ----------------------------------------
    //    @Query 예제
    //    1) 오라클 쿼리
    //    2) 객체 쿼리
    //    ----------------------------------------
    //    todo 1) ename like 검색
    @Query(value = "SELECT TD.* FROM TB_EMP TD WHERE TD.ENAME LIKE '%' || :dname || '%'", nativeQuery = true)
    List<Emp> selectByEname(@Param("ename") String ename);

위 코드를 다르게 작성하는 방법(JDK 버전에 따라 사용 불가능할 수도 있음)

//    todo: 1-1) 위의 코드를 다르게 코딩
    @Query(value = "SELECT TD.* FROM TB_DEPT TD WHERE TD.DNAME LIKE '%'||:dname||'%'",
            nativeQuery = true)
    List<Dept> selectByDname(String dname);
//    todo: 1-2) 위의 코드를 다르게 코딩 : 참고
@Query(value = "SELECT TD.* FROM TB_DEPT TD WHERE TD.DNAME LIKE '%'|| ?1 ||'%'",
        nativeQuery = true)
List<Dept> selectByDname(String dname);

✅ SQL에서 쿼리문 결과가 올바른지 확인

📂 DeptService.java

/** 전체조회 + dname like 검색 : @Query */
public List<Dept> selectByDname(String dname) {
    List<Dept> list
            = deptRepository.selectByDname(dname);

    return list;
}

📂 DeptController.java

 /** 전체조회 + dname like 검색 : @Query */
    @GetMapping("/dept/dname/{dname}")
    public ResponseEntity<Object> selectByDname(
            @PathVariable String dname
    ) {
        try {
//         전체 조회 + 정렬(dno 오름차순) 호출
            List<Dept> list
                    = deptService.selectByDname(dname);

            if (list.isEmpty() == false) {
//                성공
                return new ResponseEntity<>(list, HttpStatus.OK);
            } else {
//                데이터 없음
                return new ResponseEntity<>(HttpStatus.NO_CONTENT);
            }

        } catch (Exception e){
            log.debug(e.getMessage());
            return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

✅ 실행결과

부서명 중에 'S'가 들어가는 부서명 검색하기

GET http://localhost:8000/dept/dname/S

HTTP/1.1 200 
Content-Type: application/json
Transfer-Encoding: chunked
Date: Wed, 18 Oct 2023 00:11:16 GMT
Keep-Alive: timeout=60
Connection: keep-alive

[
  {
    "insertTime": "2023-10-18 00:11:06",
    "updateTime": null,
    "dno": 20,
    "dname": "RESEARCH",
    "loc": "DALLAS"
  },
  {
    "insertTime": "2023-10-18 00:11:06",
    "updateTime": null,
    "dno": 30,
    "dname": "SALES",
    "loc": "CHICAGO"
  },
  {
    "insertTime": "2023-10-18 00:11:06",
    "updateTime": null,
    "dno": 40,
    "dname": "OPERATIONS",
    "loc": "BOSTON"
  }
]
응답 파일이 저장되었습니다.
> 2023-10-18T091116.200.json

Response code: 200; Time: 277ms (277 ms); Content length: 295 bytes (295 B)

✅ 객체쿼리를 이용한 쿼리문 작성

//    todo: 2) dname like 검색 (객체 쿼리)
    // 객체 쿼리 만드는 방법 : 테이블 -> 클래스명
    //                      컬럼명 -> 속성명
    //                      * -> 사용하지 않음, 클래스명의 별칭을 붙여서 사용
    //                      (대소문자를 구분함, nativeQuery=true(생략))
    @Query(value = "SELECT TD FROM Dept TD WHERE TD.dname LIKE '%'||:dname||'%'")
    List<Dept> selectByDname(@Param("dname") String dname);

 

✅ TODO) 부서 테이블에서 부서명(dname)과 위치(loc)를 매개변수로 받아 조회하는 함수 만들기

📂 DeptRepository.java

@Query(value = "SELECT D.* FROM TB_DEPT D " +
        "WHERE D.DNAME = :dname " +
        "AND   D.LOC = :loc ", nativeQuery = true)
List<Dept> selectByDnameAndLoc(@Param("dname") String dname,@Param("loc") String loc);

📂 DeptService.java

/** 전체조회 + dname like 검색 : @Query */
public List<Dept> selectByDnameAndLoc(String dname, String loc) {
    List<Dept> list
            = deptRepository.selectByDnameAndLoc(dname, loc);

    return list;
}

📂 DeptController.java

    /** 전체조회 + dname like 검색 : @Query */
    @GetMapping("/dept/dname/{dname}/loc/{loc}")
    public ResponseEntity<Object> selectByDnameAndLoc(
            @PathVariable String dname,
            @PathVariable String loc
    ) {
        try {
//         전체 조회 + 정렬(dno 오름차순) 호출
            List<Dept> list
                    = deptService.selectByDnameAndLoc(dname, loc);

            if (list.isEmpty() == false) {
//                성공
                return new ResponseEntity<>(list, HttpStatus.OK);
            } else {
//                데이터 없음
                return new ResponseEntity<>(HttpStatus.NO_CONTENT);
            }

        } catch (Exception e){
            log.debug(e.getMessage());
            return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

 

반응형

'Spring Boot > 스프링부트 예제' 카테고리의 다른 글

JPA - 연관관계 매핑  (0) 2023.10.19
JPA 페이징 처리  (1) 2023.10.18
JPA 상세조회, 저장함수, 수정함수, 삭제함수  (0) 2023.10.17
JPA를 활용한 CRUD 구현하기  (0) 2023.10.16
JSTL 라이브러리  (0) 2023.10.06
반응형

안녕하세요!

이번 시간에는 JPA 를 활용한 예제 프로젝트를 생성해보겠습니다.

 

들어가기 전에

JPA의 특징으로는 SQL문을 자동생성하는 것이 특징입니다.

 

💡 프로젝트 준비 및 환경설정


프로젝트 준비시 스프링부트의 종속성을 추가합니다.

생성 후 시간이 소요됩니다.

 

한글 깨짐 방지를 위해 설정 - 에디터 - 파일 인코딩 부분에 UTF-8로 변경해줍니다.

build.gradle 파일의 dependencies 부분에  오라클과 logback, log4jdbc 종속성을 추가해줍니다.

	//   todo: 오라클 라이브러리( 19c )
	implementation 'com.oracle.database.jdbc:ucp:19.14.0.0'
	implementation 'com.oracle.database.security:oraclepki:19.14.0.0'
	implementation 'com.oracle.database.security:osdt_cert:19.14.0.0'
	implementation 'com.oracle.database.security:osdt_core:19.14.0.0'
	//   todo: logback , log4jdbc 설정
	implementation 'org.bgee.log4jdbc-log4j2:log4jdbc-log4j2-jdbc4.1:1.16'
	implementation 'ch.qos.logback:logback-classic:1.2.11'
	implementation 'org.slf4j:slf4j-api:1.7.36'
	implementation 'org.slf4j:jcl-over-slf4j:1.7.36'

 

 

그리고 main > resources > application.properties 파일에 설정을 추가합니다.

# 서버 포트
server.port=8000


# todo : docker db 설정
spring.datasource.driver-class-name=net.sf.log4jdbc.sql.jdbcapi.DriverSpy

# todo : spring.datasource.url=jdbc:log4jdbc:oracle:thin:@ip주소:db포트번호/db이름
spring.datasource.url=jdbc:log4jdbc:oracle:thin:@localhost:1521/xepdb1

spring.datasource.username=scott
spring.datasource.password=!Ds1234567890

# todo : jpa 설정
# todo : ddl-auto - create(모든 테이블 재생성)/update(수정된 테이블만 생성)/none(안함)
spring.jpa.hibernate.ddl-auto=create
#spring.jpa.hibernate.ddl-auto=update
#spring.jpa.hibernate.ddl-auto=none

# todo : db 제품 연결 ( oracle 12 이상 : Oracle12cDialect )
spring.jpa.database-platform=org.hibernate.dialect.Oracle12cDialect

# todo : generate-ddl=true (테이블 자동 생성 옵션)
spring.jpa.generate-ddl=true

# todo : sql log 보기 (true/false)
spring.jpa.show-sql=true

# 1) resource/data.sql 자동 실행 ( DML 실행 )
#  -> data.sql ( dml 실행 ), schema.sql ( ddl 실행 )
# todo : DML, DDL 스크립트 (실습용 샘플 테이블) 실행을 위한 옵션
spring.jpa.defer-datasource-initialization=true

# todo :  sql log 좀 더 정리된 형태로 찍기
spring.jpa.properties.hibernate.format_sql=true

# todo : 로깅 레벨 : error < info < debug < trace (정보 많은 순서)
logging.level.org.hibernate=info

# 2)  resource/data.sql 자동 실행 ( DML 실행 )
#  -> data.sql ( dml 실행 ), schema.sql ( ddl 실행 )
spring.sql.init.mode=always

# sql 에러 무시하고 스프링 서버 로딩
spring.sql.init.continue-on-error=true


# 자바 소스 변경시 스프링 서버 자동 재시작
spring.devtools.restart.enabled=true

# PUT , DELETE 방식도 form 에서 사용할 수 있게 만들어줌 : jsp에서 사용함
spring.mvc.hiddenmethod.filter.enabled=true

실습을 위해 resources 폴더 아래에 4가지 파일을 추가합니다.

 

📂 data.sql

INSERT INTO TB_DEPT VALUES (10,'ACCOUNTING','NEW YORK',  TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'), null);
INSERT INTO TB_DEPT VALUES (20,'RESEARCH','DALLAS',  TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'), null);
INSERT INTO TB_DEPT VALUES (30,'SALES','CHICAGO',  TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'), null);
INSERT INTO TB_DEPT VALUES (40,'OPERATIONS','BOSTON',  TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'), null);

INSERT INTO TB_EMP VALUES (7369,'SMITH','CLERK',    7902,TO_CHAR(to_date('17-12-1980','dd-mm-yyyy'), 'YYYY-MM-DD HH24:MI:SS'),800, NULL,20, TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'), NULL);
INSERT INTO TB_EMP VALUES (7499,'ALLEN','SALESMAN', 7698,TO_CHAR(to_date('20-2-1981', 'dd-mm-yyyy'), 'YYYY-MM-DD HH24:MI:SS'),1600, 300,30, TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'), NULL);
INSERT INTO TB_EMP VALUES (7521,'WARD','SALESMAN',  7698,TO_CHAR(to_date('22-2-1981', 'dd-mm-yyyy'), 'YYYY-MM-DD HH24:MI:SS'),1250, 500,30, TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'), NULL);
INSERT INTO TB_EMP VALUES (7566,'JONES','MANAGER',  7839,TO_CHAR(to_date('2-4-1981',  'dd-mm-yyyy'), 'YYYY-MM-DD HH24:MI:SS'),2975,NULL,20, TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'), NULL);
INSERT INTO TB_EMP VALUES (7654,'MARTIN','SALESMAN',7698,TO_CHAR(to_date('28-9-1981', 'dd-mm-yyyy'), 'YYYY-MM-DD HH24:MI:SS'),1250,1400,30, TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'), NULL);
INSERT INTO TB_EMP VALUES (7698,'BLAKE','MANAGER',  7839,TO_CHAR(to_date('1-5-1981',  'dd-mm-yyyy'), 'YYYY-MM-DD HH24:MI:SS'),2850,NULL,30, TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'), NULL);
INSERT INTO TB_EMP VALUES (7782,'CLARK','MANAGER',  7839,TO_CHAR(to_date('9-6-1981',  'dd-mm-yyyy'), 'YYYY-MM-DD HH24:MI:SS'),2450,NULL,10, TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'), NULL);
INSERT INTO TB_EMP VALUES (7788,'SCOTT','ANALYST',  7566,TO_CHAR(to_date('13-07-1987','dd-mm-yyyy'), 'YYYY-MM-DD HH24:MI:SS'),3000,NULL,20, TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'), NULL);
INSERT INTO TB_EMP VALUES (7839,'KING','PRESIDENT', NULL,TO_CHAR(to_date('17-11-1981','dd-mm-yyyy'), 'YYYY-MM-DD HH24:MI:SS'),5000,NULL,10, TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'), NULL);
INSERT INTO TB_EMP VALUES (7844,'TURNER','SALESMAN',7698,TO_CHAR(to_date('8-9-1981',  'dd-mm-yyyy'), 'YYYY-MM-DD HH24:MI:SS'),1500,   0,30, TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'), NULL);
INSERT INTO TB_EMP VALUES (7876,'ADAMS','CLERK',    7788,TO_CHAR(to_date('13-07-1987','dd-mm-yyyy'), 'YYYY-MM-DD HH24:MI:SS'),1100,NULL,20, TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'), NULL);
INSERT INTO TB_EMP VALUES (7900,'JAMES','CLERK',    7698,TO_CHAR(to_date('3-12-1981', 'dd-mm-yyyy'), 'YYYY-MM-DD HH24:MI:SS'),950, NULL,30, TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'), NULL);
INSERT INTO TB_EMP VALUES (7902,'FORD','ANALYST',   7566,TO_CHAR(to_date('3-12-1981', 'dd-mm-yyyy'), 'YYYY-MM-DD HH24:MI:SS'),3000,NULL,20, TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'), NULL);
INSERT INTO TB_EMP VALUES (7934,'MILLER','CLERK',   7782,TO_CHAR(to_date('23-1-1982', 'dd-mm-yyyy'), 'YYYY-MM-DD HH24:MI:SS'),1300,NULL,10, TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'), NULL);


INSERT INTO TB_FAQ VALUES (1,'제목','해결방법',  TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'), null);
INSERT INTO TB_FAQ VALUES (2,'제목2','해결방법2',  TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'), null);
INSERT INTO TB_FAQ VALUES (3,'제목3','해결방법3',  TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'), null);
INSERT INTO TB_FAQ VALUES (4,'제목4','해결방법4',  TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'), null);

COMMIT;

📂log4jdbc.log4j2.properties

# sql \uB85C\uADF8 \uC124\uC815
log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator
# \uCD5C\uB300 \uBA87 \uB77C\uC778\uAE4C\uC9C0 \uCD9C\uB825\uD560 \uAC83\uC778\uAC00\uB97C \uACB0\uC815 : 0 \uC774\uBA74 \uC81C\uD55C\uC5C6\uC74C
log4jdbc.dump.sql.maxlinelength=0

📂 logback-spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">

    <!-- Appenders -->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
       <encoder>
          <Pattern>%d %5p [%c] %m%n</Pattern>
       </encoder>
    </appender>

    <appender name="console-infolog" class="ch.qos.logback.core.ConsoleAppender">
       <encoder>
          <Pattern>%d %5p %m%n</Pattern>
       </encoder>
    </appender>

    <!-- Logger -->
    <logger name="com.example.jpaexam" level="DEBUG" appender-ref="console" />
    <logger name="jdbc.sqlonly" level="INFO" appender-ref="console-infolog" />
    <logger name="jdbc.resultsettable" level="INFO" appender-ref="console-infolog" />

    <!-- Root Logger : 기본 OFF -->
<!--    하위 로거에서 재정의해서 사용함 : INFO, DEBUG 등으로 -->
    <root level="off">
       <appender-ref ref="console" />
    </root>
</configuration>

📂 shema.sql

DROP SEQUENCE SQ_DEPT;
CREATE SEQUENCE SQ_DEPT START WITH 50 INCREMENT BY  10;

DROP SEQUENCE SQ_EMP;
CREATE SEQUENCE SQ_EMP START WITH 8000 INCREMENT BY  1;

DROP SEQUENCE SQ_FAQ;
CREATE SEQUENCE SQ_FAQ START WITH 1 INCREMENT BY  1;

DROP TABLE TB_EMP CASCADE CONSTRAINT;
DROP TABLE TB_DEPT CASCADE CONSTRAINT;
DROP TABLE TB_FAQ CASCADE CONSTRAINT;

CREATE TABLE TB_DEPT (
                         DNO NUMBER NOT NULL PRIMARY KEY,
                         DNAME VARCHAR2(255),
                         LOC VARCHAR2(255),
                         INSERT_TIME VARCHAR2(255),
                         UPDATE_TIME VARCHAR2(255)
);

CREATE TABLE TB_EMP
(ENO NUMBER NOT NULL PRIMARY KEY,
 ENAME VARCHAR2(255),
 JOB   VARCHAR2(255),
 MANAGER  NUMBER,
 HIREDATE VARCHAR2(255),
 SALARY NUMBER,
 COMMISSION NUMBER,
 DNO NUMBER,
 INSERT_TIME VARCHAR2(255),
 UPDATE_TIME VARCHAR2(255)
);

CREATE TABLE TB_FAQ (
                        NO NUMBER NOT NULL PRIMARY KEY,
                        TITLE VARCHAR2(255),
                        CONTENT VARCHAR2(255),
                        INSERT_TIME VARCHAR2(255),
                        UPDATE_TIME VARCHAR2(255)
);

 

그리고 java > com.example.jpaexam 폴더 아래에 다음과 같이 폴더를 생성합니다.

 

💡 model 폴더에 공통모델 파일 생성


 

model 폴더 아래에 생성일자/수정일자를 만들어주는 추상클래스를 생성합니다.

실습에서는 파일명을 BaseTimeEntity 로 생성하였습니다.

📂 model > BaseTimeEntity.java

1) 먼저 추상클래스 위에 어노테이션 3개를 추가합니다.

package com.example.jpaexam.model;

import lombok.Getter;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;

/**
 * packageName : com.example.jpaexam.model
 * fileName : BaseTimeEntity
 * author : GGG
 * date : 2023-10-16
 * description : JPA 에서 자동으로 생성일자/수정일자를 만들어주는 클래스
 * 요약 :
 * <p>
 * ===========================================================
 * DATE            AUTHOR             NOTE
 * —————————————————————————————
 * 2023-10-16         GGG          최초 생성
 */
@Getter
// todo : 자동으로 생성일자/수정일자 컬럼을 sql 문에 추가시키는 어노테이션 2개
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseTimeEntity {
    // todo) 공통 속성
    private  String insertTime;
    private  String updateTime;
}

 

여기서 @MappedSuperclass 어노테이션의 의미는

생성일자/수정일자를 모든 엔티티에 공통으로 가져가야하는 상황에서 위의 코드처럼

BaseTimeEntity를 정의해서 활용할 때 사용합니다.

 

2) 공통 속성의 형태를 변경하는 함수 추가

위의 코드는 time의 형태가 yyyy-MM-dd HH:mm:ss 형태가 아닌 기본 형태로 보이게 됩니다.

가독성을 좋게하기 위해 공통속성 코드 아래에 함수를 추가합니다.

 

...
private String updateTime;

// todo) 해당 테이블에 데이터가 만들어 질 때(insert 문) 실행되는 이벤트 함수
@PrePersist
void OnPerPersist(){
    this.insertTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));

}
// todo) 해당 테이블에 데이터가 수정될 때(update 문) 실행되는 이벤트 함수
@PreUpdate
void OnPreUpdate(){
    this.updateTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
    this.insertTime = this.updateTime; // 생성일시 == 수정일시 동일하게 처리
}

 

💡 Dept 모델 생성


model 폴더 아래에 Dept 클래스를 생성합니다.

JPA에서 사용하는 어노테이션의 설명은 아래 코드에 todo로 표시되어있습니다.

📂 model > Dept.java

package com.example.jpaexam.model;

import lombok.*;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;

import javax.persistence.*;

/**
 * packageName : com.example.jpaexam.model
 * fileName : Dept
 * author : GGG
 * date : 2023-10-16
 * description : 부서 모델 클래스 ( 엔티티(entity) )
 * 요약 :
 * <p>
 * ===========================================================
 * DATE            AUTHOR             NOTE
 * —————————————————————————————
 * 2023-10-16         GGG          최초 생성
 */
// todo) @Entity - JPA 기능을 클래스에 부여하는 어노테이션
@Entity
// todo) @Table(name = "생성될 테이블명")
@Table(name = "TB_DEPT")
// todo 사용법 : @SequenceGenerator(
//        name = "시퀀스 함수 이름"
//        , sequenceName = "DB에 생성된 시퀀스 이름"
//        , initialValue = 시작값
//        , allocationSize = jpa에서 관리용숫자(성능지표)
//  )
@SequenceGenerator(
        name = "SQ_DEPT_GENERATOR"
        , sequenceName = "SQ_DEPT"
        , initialValue = 1
        , allocationSize = 1
)
@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
// todo) JPA 어노테이션 SQL 자동 생성시 null 값 컬럼은 제외하고 생성하는 어노테이션
//  예) insert into 테이블명(컬럼1, 컬럼2, 컬럼3) values(1, 2, null)
//    => insert into 테이블명(컬럼1, 컬럼2) values(1, 2)
@DynamicInsert
@DynamicUpdate
public class Dept extends BaseTimeEntity{
    @Id // todo) 기본키임을 알려주는 어노테이션
    // todo) @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "시퀀스함수이름")
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SQ_DEPT_GENERATOR") // 시퀀스 기능 부여
    // todo : @Column(columnDefinition = "DB컬럼 자료형")
    @Column(columnDefinition = "NUMBER")
    private Integer dno;    // 부서번호(기본키) - 시퀀스 기능 부여

    @Column(columnDefinition = "VARCHAR2(255)")
    private String dname;   // 부서명

    @Column(columnDefinition = "VARCHAR2(255)")
    private String loc;     // 부서위치
}

 

repository 파일 만들기

DB 접속 함수들(CRUD)을 만들어주기 위한 인터페이스 파일을 생성합니다.

📂 repository > DeptRepository.java 

package com.example.jpaexam.repository;

import com.example.jpaexam.model.Dept;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

/**
 * packageName : com.example.jpaexam.repository
 * fileName : DeptRepository
 * author : GGG
 * date : 2023-10-16
 * description : JPA 레포지토리 인터페이스 (DB 접속 함수들(CRUD) 있음)
 *                  == DAO랑 비슷함
 * 요약 :
 * <p>
 * ===========================================================
 * DATE            AUTHOR             NOTE
 * —————————————————————————————
 * 2023-10-16         GGG          최초 생성
 */
// todo) @Repository - 클래스위에 붙이고, 스프링서버가 실행될 때 자동으로 객체 1개를 만들어줌( IOC )
//    사용법 : 인터페이스명 extends JpaRepository<모델클래스명, 기본키의 자료형>
@Repository
public interface DeptRepository extends JpaRepository<Dept, Integer> {
}

JpaRepository를 상속받으면 DB접속에 사용할 수 있는 CRUD함수 기능을 사용할 수 있습니다.

 

💡 업무 service 클래스 생성

📂 service > DeptService.java

package com.example.jpaexam.service;

import com.example.jpaexam.model.Dept;
import com.example.jpaexam.repository.DeptRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * packageName : com.example.jpaexam.service
 * fileName : DeptService
 * author : GGG
 * date : 2023-10-16
 * description : 부서 업무 서비스
 * 요약 :
 * <p>
 * ===========================================================
 * DATE            AUTHOR             NOTE
 * —————————————————————————————
 * 2023-10-16         GGG          최초 생성
 */
@Service
public class DeptService {
    
    @Autowired
    DeptRepository deptRepository;  // DI 객체 가져오기
    
    /** 전체 조회 */
    public List<Dept> findAll(){
        List<Dept> list = deptRepository.findAll(); // db 전체조회 함수 호출
        return list;
    }
    
}

 

💡 controller 생성

📂 controller > DeptController

package com.example.jpaexam.controller.exam01;

import com.example.jpaexam.model.Dept;
import com.example.jpaexam.service.DeptService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
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;

/**
 * packageName : com.example.jpaexam.controller.exam01
 * fileName : DeptController
 * author : GGG
 * date : 2023-10-16
 * description : 부서 컨트롤러 : @RestController 사용
 * 요약 :
 * <p>
 * ===========================================================
 * DATE            AUTHOR             NOTE
 * —————————————————————————————
 * 2023-10-16         GGG          최초 생성
 */
@Slf4j
@RestController
@RequestMapping("/exam01")
public class DeptController {

    @Autowired
    DeptService deptService;    // 객체 가져오기 (DI)

    /** 전체조회 함수 */
    @GetMapping("/dept")
    public ResponseEntity<Object> getDeptAll() {
        try {
            // todo) 전체조회 함수 호출
            List<Dept> list = deptService.findAll();
            if (list.isEmpty() == false) {
                // todo) 성공
                return new ResponseEntity<>(list, HttpStatus.OK);
            } else {
                // todo) 데이터 없음
                return new ResponseEntity<>(HttpStatus.NO_CONTENT);
            }
        } catch (Exception e) {
            return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
}

REST API 테스트를 위해 아래 그림의 빨간색 박스를 누릅니다.

테스트 창에서 ▶ 버튼을 눌러 실행합니다.

실행결과

GET http://localhost:8000/exam01/dept

HTTP/1.1 200 
Content-Type: application/json
Transfer-Encoding: chunked
Date: Mon, 16 Oct 2023 08:15:45 GMT
Keep-Alive: timeout=60
Connection: keep-alive

[
  {
    "insertTime": "2023-10-16 08:15:40",
    "updateTime": null,
    "dno": 10,
    "dname": "ACCOUNTING",
    "loc": "NEW YORK"
  },
  {
    "insertTime": "2023-10-16 08:15:40",
    "updateTime": null,
    "dno": 20,
    "dname": "RESEARCH",
    "loc": "DALLAS"
  },
  {
    "insertTime": "2023-10-16 08:15:40",
    "updateTime": null,
    "dno": 30,
    "dname": "SALES",
    "loc": "CHICAGO"
  },
  {
    "insertTime": "2023-10-16 08:15:40",
    "updateTime": null,
    "dno": 40,
    "dname": "OPERATIONS",
    "loc": "BOSTON"
  }
]
응답 파일이 저장되었습니다.
> 2023-10-16T171545.200.json

Response code: 200; Time: 297ms (297 ms); Content length: 397 bytes (397 B)

JPA에서 자동으로 만든 select 문을 확인해봅시다.

Hibernate: 
    select
        dept0_.dno as dno1_0_,
        dept0_.insert_time as insert_time2_0_,
        dept0_.update_time as update_time3_0_,
        dept0_.dname as dname4_0_,
        dept0_.loc as loc5_0_ 
    from
        tb_dept dept0_

 

반응형
반응형

💡 Core 라이브러리

JSTL의 core 라이브러리를 사용하기 위해서는 JSP 파일 상단에 코드를 선언합니다.

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

📖 <c:set> : 변수에 값을 저장

변수저장소(사용범위)
page(현재페이지) < request < session < application(전체프로그램)
사용법 : <c:set var="변수명" value="값" scope="page"(scope는 생략가능, 기본이 page)/>

📌 사용 예제

<%--     TODO : jstl 표현식--%>
        <p><c:set var="num" value="10" scope="page"/></p>
        <p><c:set var="num2" value="10" scope="page"/></p>
        <p><c:set var="total" value="${num + num2}" scope="page"/></p>
<%--     TODO : el 표현식--%>
        <p>${num}</p>
        <p>${num2}</p>
        <p>${total}</p>

📌 결과

📖 <c:forTokens> : 문자열 구분자로 구분하여 전체 출력하기

사용법 : 
<c:forTokens items="문자열" delims="구분자" var="변수명">
         실행문
</c:forTokens>

📌 사용예제

<c:forTokens items="1/2/3" delims="/" var="number">
    <h2>토큰 : ${number}</h2>
</c:forTokens>

📌 실행결과

📖 <c:catch> : try ~ catch 예외처리

사용법 : 
<c:catch var="에러변수"> 실행문 </c:catch>
실행문이 에러 발생하면 에러변수에 메세지가 들어감

📌 사용예제

<c:set var="num3" value="20" />
<c:catch var="err">
    <h2>${num3}</h2>
</c:catch>
<c:if test="${not empty err}">
    에러 메세지 : ${err.message}
</c:if>

📌 실행결과

📖 <c:redirect> : 페이지 강제이동

<c:redirect url="이동할_url주소" />

 

📖 <c:import> : 공통페이지 끼워 넣기

<c:import url="보여줄페이지" />

📌 사용예제

📖 <c:url> : url로 페이지 이동하기

<c:url value="url주소" var="변수명" />
[사용]
<a href="${변수명}">표시할내용</a>

📌 사용예제

 

💡 Function 라이브러리

fn 라이브러리를 사용하기전 상단 코드 선언

fn 라이브러리는 el 표현식과 함께 사용합니다.

<%--TODO: fn lib import--%>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>

<%-- TODO: fmt lib import--%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

📌 변수 선언

<c:set var="chars" value="안녕하세요 홍길동입니다. Bye"/>
<c:set var="name" value="홍길동"/>

 

📖 fn:contains(string, substr)

string이 substr을 포함하면 return true 없으면 false return

<h2>indexOf : ${fn:indexOf(chars,"홍길동")}</h2>

📖 fn:indexOf(string, substr)

string 문자열에 "substr" 값이 있는 index 번호를 return

<h2>indexOf : ${fn:indexOf(chars,"홍길동")}</h2>

📖 fn:length(string)

전체 길이를 리턴

<h2>length : ${fn:length(chars)}</h2>

📖 fn:replace(string, "기존문자열", "바꿀문자열")

string 문자열에서 기존문자열을 바꿀문자열로 변경

<h2>replace : ${fn:replace(chars,"홍길동","장길산")}</h2>

📖 fn:split(string, 구분자)

string에서 구분자로 문자열 자르기, 리턴은 배열

<h2><c:set var="sChar" value='${fn:split(chars, "")}'/></h2>
<h2>${sChar[0]}</h2>
<h2>${sChar[1]}</h2>
<h2>${sChar[2]}</h2>

📖 fn:substring(string, 시작인덱스번호, 끝인덱스번호)

string 문자열에서 시작인덱스에서 끝인덱스번호-1까지 자르기

<h2>substring : ${fn:substring(chars,0 ,6)}</h2>

📖 fn:toLowerCase/toUpperCase(string)

대소문자 바꾸기

<h2>toLowerCase : ${fn:toLowerCase(chars)}</h2>
<h2>toUpperCase : ${fn:toUpperCase(chars)}</h2>

 

💡 fmt(format) 라이브러리

fmt 라이브러리는 날짜,시간, 숫자 텍스트를 포멧하고 표시하는 fomatter 기능을 제공합니다.

 

📌 변수선언

<c:set var="numtest" value="1234567890"/>

📖 fmt:formatNumber : 세 자리마다 쉼표(,) 추가하여 출력

<h2><fmt:formatNumber value="${numtest}"/> </h2>

1) 숫자에 통화 추가 : value 뒤에 type="currency" 추가

<h2>formatNumber : <fmt:formatNumber value="${numtest}" type="currency"/></h2>

📖 날짜 포멧 주기

날짜 포멧을 주기위해서는 스크립틀릿을 사용합니다. 스크립틀릿은 과거 코딩기법이라 사용은 하지 않습니다.

<%

  자바코딩 가능

 %>

 <%
        Date date = new java.util.Date();
    %>
<%--       TODO: 화면 출력시 : <%=변수명%>--%>
    <h2><%=date%></h2>
    <c:set var="today" value="<%=new java.util.Date()%>" />
    <h2><fmt:formatDate value="${today}" pattern="yyyy-MM-dd hh:mm:ss" /> </h2>

반응형
반응형

💡 파라미터 방식

앞 전 예제에서는 쿼리스트링 방식으로 매개변수를 전달하였습니다. 이번에는 쿼리스트링 방식을 개선한 파라미터 방식의 매개변수 전달 방법에 대해 알아보겠습니다.

 

📂PathVariableController.java

@Controller
@RequestMapping("/exam05")
public class PathVariableController {
//    TODO : url 테스트 파라미터 방식 : http://localhost:8000/exam05/path-variable/LeeJunHyuk
//                                  => @GetMapping("/path-variable/{웹매개변수명}")
//                                  => 웹 브라우저 주소창 사용 : url/값
//    TODO : url 테스트 쿼리스트링 방식 : http://localhost:8000/exam05/path-variable?name=LeeJunHyuk
    @GetMapping("/path-variable/{name}")
    public String getPathVariable(
            @PathVariable String name,
            Model model)
    {
        model.addAttribute("name", name);
        return "exam05/path_variable.jsp";
    }
}

📂path_variable.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%--TODO : JSTL 표현식 사용을 위한 import--%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<%-- TODO : el 표현식--%>
    <p>${name}</p>
</body>
</html>

파라미터 형식의 매개변수 전달방법은

@GetMapping("url/{웹매개변수명}") 으로 사용하고 메소드 매개변수로 @PathVariable를 사용한 것을 알 수 있습니다.

 

📌 실행결과

반응형
반응형

💡 JSP의 조건문, URL 매개변수에 3개를 전달해서 조건에 따라 출력하기

📂 MultiParamController.java

   TODO : 예제 3) JSP의 조건문, URL 매개변수 3개를 전달해서 조건에 따라 출력하기
//     URL 테스트 : http://localhost:8000/exam03/condition?name=삼식이&color=brown&kind=dog
    @GetMapping("/condition")
    public String setCondition(Model model,
                               @RequestParam(defaultValue = "") String name,
                               @RequestParam(defaultValue = "") String color,
                               @RequestParam(defaultValue = "") String kind)
    {
        model.addAttribute("name", name);
        model.addAttribute("color", color);
        model.addAttribute("kind", kind);
        return "/exam03/condition.jsp";
    }
}

📂 condition.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%--TODO : JSTL 표현식 사용을 위한 import--%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <%--    TODO 부트스트랩 CSS CDN --%>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet"
          integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
</head>
<body>
    <div class="container">
        <h2>매개변수(파라메터) 전달받아 출력하기</h2>
        <p>${name}</p>
        <p>${color}</p>
        <p>${kind}</p>

<%--        TODO : if 조건문으로 출력하기: jstl 표현식, else 없음 사용--%>
<%--         사용법 : <c:if test="${조건식}">실행문</c:if>--%>
        <c:if test="${name == '삼식이'}">
            <p>삼식이가 맞습니다.</p>
        </c:if>
    </div>
</body>
</html>

✅ JSP의 if 조건문

JSP에서 if 조건문을 사용할 경우에는 JSTL 표현식을 사용합니다. 사용법은 아래와 같습니다.

<c:if test="${조건식}> 실행문 </c:if>

단 JSP의 if 조건문은 JAVA에서 처럼 else구문이 따로 없는것이 특징입니다.

 

그럼 else 구문을 사용하려면 어떻게 해야할까요?

 

✅ JSP의 choose 조건문

choose 조건문은 JAVA의 if ~ else if 구문과 비슷합니다.

 

JAVA의 else 구문과 같은 의미인 otherwise가 있습니다.

 

📌 choose 조건문

<%--        TODO : choose 조건문으로 출력하기, else 있음--%>
        <c:choose>
            <c:when test="${color == 'brown'}">
                갈색입니다.
            </c:when>
            <c:when test="${color == 'yellow'}">
                노란색입니다.
            </c:when>
            <c:otherwise>
                다른색입니다.
            </c:otherwise>
        </c:choose>

📌 사용법

// TODO: choose 조건문으로 출력하기 , else 있음

사용법 
<c:choose>
(if, else if): <c:when test="조건문">실행문</c:when>
(else): <c:otherwise>실행문</c:otherwise>
</c:choose>

📌 출력결과

반응형
반응형

💡 모델을 통해 변수 등록하는 방법

모델은 컨트롤러의 메서드에서 매개변수로 받아옵니다. 모델에서 변수를 등록할 때는 addAttribute() 메서드를 사용합니다.

model.addAttribute("변수명", 변숫값);

📂 HelloController.java

package com.example.controllerexam.controller.exam01;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

/**
 * packageName : com.example.controllerexam.controller.exam01
 * fileName : HelloController
 * author : GGG
 * date : 2023-10-04
 * description : JSP, 타임리프  : SSR  (Server Side Rendering)
 *                   1) 구조 => Springboot 소스가 같이 있음
 *                   2) html 파일을 Springboot에서 만들어서 웹브라우저로 전송함
 *               React / Vue  : CSR  (Client Side Rendering)
 *                   1) 구조 => 소스가 분리됨
 *                   2) html 파일을 최초 1회 전송하고
 *                     => 다음부터는 변경되는 부분만 서버에서 받고, 나머지는 웹 브라우저에서 자체 만듦
 *               @GetMapping("url")함수(){ return "jsp페이지명"; }
 *               => CRUD => Read 요청일때 사용하는 어노테이션
 * 요약 :
 * <p>
 * ===========================================================
 * DATE            AUTHOR             NOTE
 * —————————————————————————————
 * 2023-10-04         GGG          최초 생성
 */
// TODO 1) @Controller 붙이기
@Controller
public class HelloController {
    @GetMapping("/exam01/hello")
    public String Hello(Model model){
//     TODO: 해쉬맵 자료구조 : (키, 값) => jsp 페이지로 변수를(greeting, greeting2 ...) 전송
        model.addAttribute("greeting","안녕 Springboot");
        model.addAttribute("greeting2","Springboot 처음이지");
        model.addAttribute("greeting3","수고해!!!");

        return "exam01/hello.jsp";
    }
}

💡 JSP의  el (Expression Language) 표현식

el(Expression Language) 표현식은 자바 Spring에서 전송한(Model) 키 값을 출력할 수 있습니다.

${변수명}

 

📂 hello.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%--TODO : JSTL 표현식 사용을 위한 import--%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>


<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
<%--    TODO 부트스트랩 CSS CDN --%>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet"
          integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
</head>
<body>
<%-- jsp 주석 --%>
<%-- TODO: el(Expression Language) 표현식 : ${변수명} --%>
<%--        자바 Spring에서 전송한(Model) 키 값을 출력할 수 있음--%>
    <p>${greeting}</p>
    <p>${greeting2}</p>
    <p>${greeting3}</p>

<%-- TODO: JSTL 표현식(참고) : <c:out value="${변수명}" /> --%>
    <p><c:out value="${greeting}" /></p>
</body>
</html>

 

📌 실행결과

💡 JSP 페이지로 모든 자료형을 보내기

//    TODO: 예제 2) JSP 페이지로 모든 자료형을 보내기
//         url : /exam01/operation
//         jsp : exam01/operation.jsp

✅ JSP에서 JAVA 문법과 같이 사칙연산 / 논리합(곱) / 비교연산자 / 삼항연산자 사용가능!

TODO: 문자열로 생긴 숫자는 자동으로 형변환 됨 (사칙연산시)
JSP : Java Server Page, 자바 문법 모두 사용가능
JSP(.jsp) -> servlet(.java)--%>

 

📂 HelloController.java

@GetMapping("/exam01/operation")
public String Operation(Model model) {

    String strNum = "10";
    int iNum = 100;
    String strVal = "a";
    boolean bVal = true;

    model.addAttribute("strNum",strNum);
    model.addAttribute("iNum",iNum);
    model.addAttribute("strVal",strVal);
    model.addAttribute("bVal",bVal);


    return "exam01/operation.jsp";
}

📂 operation.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%--TODO : JSTL 표현식 사용을 위한 import--%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <%--bootstrap css cdn--%>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet"
          integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
</head>
<body>
<%--TODO: spring에서 전송한 값으로 사칙연산, 논리연산 해보기--%>
<div class="container p-3">
    <h2>산술연산자</h2>
    <div class="ms-3 mt-3">
<%--   TODO: 문자열로 생긴 숫자는 자동으로 형변환 됨 (사칙연산시)    --%>
<%--    JSP : Java Server Page, 자바 문법 모두 사용가능 --%>
<%--    JSP(.jsp) -> servlet(.java)--%>
        <p>덧셈 : ${strNum + 10}</p>
        <p>뺄셈 : ${strNum - 10}</p>
        <p>곱셈 : ${strNum * 10}</p>
        <p>나눗셈 : ${strNum / 10}</p>
        <p>나머지 : ${strNum % 10}</p>

        <p>숫자 연산 : ${iNum + 10}</p>

        <p>논리 합 : ${bVal || false}</p>
        <p>논리 곱 : ${bVal && false}</p>

        <p>같음 : ${iNum == 100}</p>
        <p>같지않음 : ${iNum != 100}</p>
        <p>보다큼 : ${iNum >= 100}</p>
        <p>보다작음 : ${iNum <= 100}</p>

        <p>조건(삼항) : ${(iNum == 100)? "100임" : "100이 아님"}</p>
<%--    TODO : empty(빈) : 변수의 값이 비었으면(null, 0, "") true, 아니면 false --%>
        <p>empty : ${empty iNum}</p>
        <p>not empty : ${not empty iNum}</p>
    </div>
</div>


</body>
</html>

📌 실행결과

📖 연습문제

//  TODO: 연습 1) url : /exam01/hello2 => "exam01/example01.jsp" 에 "안녕 hello2 페이지 " 출력하기
//                                                                 "Springboot 처음이지"
//                                                                 "수고해!!!"

 

📂 HelloController.java

package com.example.controllerexam.controller.exam01;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;


// TODO 1) @Controller 붙이기
@Controller
public class HelloController {
 
//  TODO: 연습 1) url : /exam01/hello2 => "exam01/example01.jsp" 에 "안녕 hello2 페이지 " 출력하기
//                                                                 "Springboot 처음이지"
//                                                                 "수고해!!!"

    @GetMapping("/exam01/hello2")
    public String Hello2(Model model){
        model.addAttribute("greeting", "안녕 hello2 페이지");
        model.addAttribute("greeting2", "Springboot 처음이지");
        model.addAttribute("greeting3", "수고해!!!");

        return "exam01/example01.jsp";
    }

}

📂 example01.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%--TODO : JSTL 표현식 사용을 위한 import--%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>


<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <%--    TODO 부트스트랩 CSS CDN --%>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet"
          integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
</head>
<body>
<%-- jsp 주석 --%>
<%-- TODO: el(Expression Language) 표현식 : ${변수명} --%>
<%--        자바 Spring에서 전송한(Model) 키 값을 출력할 수 있음--%>
<p>${greeting}</p>
<p>${greeting2}</p>
<p>${greeting3}</p>


</body>
</html>

📌 실행결과

반응형
반응형

swith문에 대해 자세히 알아보자!


 

 

  • if 문의 경우 결과가 참, 거짓 두가지 밖에 없다.
  • 경우의 수가 많아 질 수록 else - if를 계속 추가해야해서 복잡하고, 처리시간도 길어진다.
switch 문
  • 단 하나의 조건식으로 많은 경우의 수를 처리
  • 표현도 간결해 알아보기가 쉽다.
  • 처리할 경우의 수가 많은 경우에는 if문 보다 switch문으로 작성하는 것이 좋다.
  • 단, switch문은 제약조건이 있기 때문에, 경우의 수가 많아도 어쩔 수 없이 if문으로 작성해야하는 경우가 있다.
  1. 조건식을 계산한다.
  2. 조건식의 결과와 일치하는 case문으로 이동한다.
  3. 이후의 문장들을 수행한다.
  4. break문이나 switch문의 끝을 만나면 switch문 전체를 빠져나간다.

  • 만약 조건식의 결과와 일치하는 case문이 하나도 없으면 default문으로 이동한다.
  • default문은 if 문의 else블럭과 같은 역할이라 생각하면 된다.
  • switch 문의 break 문은 각 case문의 영역을 구분하는 역할을 한다.
  • 만약 break문을 생략하게 된다면 case문 사이의 구분이 없어지는것과 같아, 다른 break 문을 만나거나 switch문 블럭{ } 끝을 만날때 까지 나오는 모든 문장들을 수행하게 된다.
  • 이러한 실수를 방지하기위해 break문을 빼먹지 않도록 주의해야한다.
switch 문의 제약조건

 

  • switch문의 조건식은 결과값이 반드시 정수이어야 한다.
  • 결과값과 일치하는 case문으로 이동하기 때문에 case문의 값 역시 정수이어야 한다.
  • case문의 값이 중복되지 않아야 한다. // 같은 값의 case문이 여러 개 이면 어디로 이동해야할지 모르기 때문!
  • case문의 값은 반드시 상수이어야 한다. // 변수나 실수는 case문의 값으로 사용할 수 없다!

public static void main(String[] args) {

int num, result;

final int ONE = 1;

 

 

switch (result) {

case '1' : // OK. 문자 리터럴(정수 49와 동일)

case ONE : // OK. 정수 상수

case "YES" : // OK. 문자열 리터럴. JDK 1.7부터 허용

case num : // 에러. 변수는 불가

case 1.0 : // 에러. 실수도 불가

 

}

switch문의 제약조건 예제
예제문은 [JAVA의 정석(기초편)_남궁성 지음] 책을 참고하였습니다.

package ch01;

 

import java.util.Scanner;

 

public class SwitchMainTest {

 

public static void main(String[] args) {

System.out.print("현재 월을 입력하세요.>");

 

Scanner sc = new Scanner(System.in);

 

int month = sc.nextInt(); // 화면을 통해 입력받은 숫자를 month에 지정

 

switch (month) {

case 3:

case 4:

case 5:

System.out.println("현재의 계절은 봄입니다.");

break;

case 6:

case 7:

case 8:

System.out.println("현재의 계절은 여름입니다.");

break;

case 9:

case 10:

case 11:

System.out.println("현재의 계절은 가을입니다.");

break;

default: // case 12: case 1: case 2:

System.out.println("현재의 계절은 겨울입니다.");

 

} // end of switch

 

} // end of main

 

} // end of class

 

* case문은 한 줄에 쓰던, 한 줄에 붙여서 쓰던 상관없음.

만약 예제의 switch 문을 if문으로 변경하면 다음과 같다.

if(month== 3 || month == 4 || month == 5) {

System.out.println("현재의 계절은 봄입니다.");

} else if (month== 6 || month == 7 || month == 8) {

System.out.println("현재의 계절은 여름입니다.");

} else if (month== 9 || month == 10 || month == 11) {

System.out.println("현재의 계절은 가을입니다.");

} else { // month == 12, 1, 2

System.out.println("현재의 계절은 겨울입니다.");

}

반응형

'Java > Java 이론' 카테고리의 다른 글

[JAVA] 다형성  (0) 2023.08.10
[JAVA] 객체지향 언어  (0) 2023.08.09
[JAVA] 상속 (2)  (0) 2023.08.08
[JAVA] 상속  (0) 2023.08.08
[JAVA] 배열  (0) 2023.08.04
반응형
캐멀(camel) 스타일

코드를 작성할 때 여러 단어를 혼합하여 명명하는 경우, 낙타의 등처럼 대소문자가 섞여있도록 작성하는 스타일을 말한다.

자바 소스 파일명과 변수명을 작성할 때 관례적으로 사용.

 

예를 들면,

 

자바 소스 파일명(클래스명)은 대문자로 시작하는 것이 관례!

 

Weekend.java

MathGrade.java

 

변수명은 소문자로 시작하는 것이 관례!

score

koreanScore

 


요약

 

캐멀스타일 

낙타의 등 처럼 대소문자가 섞여있도록 작성하는 스타일!

클래스명은 대문자로 시작, 변수명은 소문자로 시작하는 것이 관례!

반응형

'Java > Java 이론' 카테고리의 다른 글

[JAVA] 산술연산자 (2)  (0) 2023.07.26
[JAVA] 산술연산자 (1)  (0) 2023.07.26
[JAVA] 대입연산자와 부호연산자  (0) 2023.07.25
[JAVA] 형변환  (0) 2023.07.25
[JAVA] 변수와 상수  (0) 2023.07.25
반응형

프로그래밍의 개요

 

프로그래밍 이란?
컴퓨터와 대화하면서 일을 시키는 작업이라고 합니다.

내가 배우는 JAVA란 무엇일까?

자바의 아버지라고 불리는 제임스 고슬링과 그 동료들이 1991년에 그린 프로젝트란 이름으로 시작해서 1995년에 발표된 프로그래밍 언어입니다. Java는 원래 가전제품 내에 동작하는 프로그램을 위해 개발했지만 웹의 등장으로 엄청난 성공을 거두면서 프로그램 개발에 있어 주류 언어중 하나가 되었고, 안드로이드 개발에도 자바가 널리 사용되고 특히 우리 나라 대부분의 공공기관에서 조차 사용하는 프로그램은 언어이기 때문에 활용도가 굉장히 높습니다.

< 자바 프로그래밍의 특징 >  
​●  다양한 환경에서 사용할 수 있다. 
●  프로그램이 안정적이다.
●  풍부한 기능이 제공되는 언어이다.
● 객체 지향 언어이다. 
- Object oriented programming(OOP) 이라고 함   
[출처] 자바 소개|작성자 devnote1

 

 

자바 개발을 위한 용어 정리

JDK(Java Development Kit) : 자바에서 제공되는 개발용 라이브러리. 계속 버전이 올라가고 있음

JRE(Java Runtime Environment) : 자바 프로그램이 실행되는 환경. 8.0까지 무료로 제공 됨

JVM(Java Virtual Machine) : 자바 가상 머신으로 프로그램이 실행되는 환경인 JRE 가 설치되어 있어야 함

[출처] 프로그램 설치|작성자 devnote1

 

2. Java 개발의 시작

2-1) JDK 설치

설치과정은 다음과 같습니다.

보통 자바는 11.xx.xx 버전을 주로 사용합니다.

 

1) JDK 설치

오라클 홈페이지에서 Java archive를 선택

 

우측 'Java SE 11' 클릭

 

OS 환경에 맞는 설치파일을 다운 받아줍니다.

저는 윈도우를 사용하기때문에 윈도우 버전을 다운받았습니다.

 

다운받은 exe 파일을 실행시켜 설치를 진행시켜줍니다.

설치완료 후 명령프롬프트를 실행시켜 JDK가 잘 설치되었는지 확인을 해봅시다!

우선 명령프롬프트에 'java' 라는 명령어를 입력해줍니다.

입력시 위 명령어들이 주르륵 나온다면 제대로 설치가 된 상태입니다.

 

그리고 'javac' 명령어를 입력해주고,

마지막으로 'java -version' 명령어를 입력해주면 아래에 자바 버전이 뜹니다.

 

3. 환경변수 설정

만약 이러한 명령어들이 나오지 않는다면 환경변수 설정을 해주어야 합니다.

환경변수 설정을 하는 이유는 

환경변수 설정 방법은 시작버튼 옆 돋보기 or 검색창에 '시스템 환경변수'만 입력하면 제어판 설정 메뉴가 나옵니다. 

아래에 환경 변수라는 버튼을 눌러줍니다.

 

아래 시스템 변수 부분에 스크롤을 내리면 'Path' 부분을 더블클릭 해줍니다.

 

오른편 새로만들기 버튼을 눌러 경로를 지정해주는데 JDK 파일이 설치된 bin 폴더까지 경로를 찾아 복사, 붙혀넣기를 해줍니다.

저의 경우 c드라이브에 설치가 되어 있어 'C:\Program Files\Java\jdk-11\bin' 이경로로 지정을 해주었습니다.

지정 후 열려있는 창에 있는 확인 버튼을 눌러줍니다.

 

4. 명령프롬프트에서 다시 확인

명령프롬프트를 재시작(이미 켜져있는 경우 종료후 다시 시작해주세요!)하여 아래 명령어들을 입력해봅니다.

1. java : .class 파일을 실행시키는 역할

2. javac : 자바 컴파일러로 텍스트로 작성된 .class 파일로 변환시키는 역할을 합니다.

3. java -version : JDK 버전을 확인하는 명령어

반응형

'Java > Java 이론' 카테고리의 다른 글

[JAVA] 변수와 상수  (0) 2023.07.25
[JAVA] 변수형과 자료형 - 2  (0) 2023.07.25
[JAVA] 변수와 자료형  (0) 2023.07.24
[JAVA] 이클립스 환경 설정  (0) 2023.07.24
[JAVA] 이클립스(Eclipse) 설치  (0) 2023.07.24

+ Recent posts