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 서블릿으로 변환한 후 컴파일하여 실행한다.
<%@ 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 페이지를 컴파일한 결과인 서블릿 클래스를 실행한다는 의미가 된다.
/** 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 사이의 값을 같는
// 사원을 조회하려고 합니다. 함수를 작성해 주세요
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);
}
}
데이터베이스에서 값을 가져올 때는 위의 코드처럼 메서드의 이름만으로 쿼리 메서드를 생성할 수 도 있습니다.
이번에는 @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);
}
}
그리고 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
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; // 생성일시 == 수정일시 동일하게 처리
}
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_
앞 전 예제에서는 쿼리스트링 방식으로 매개변수를 전달하였습니다. 이번에는 쿼리스트링 방식을 개선한 파라미터 방식의 매개변수 전달 방법에 대해 알아보겠습니다.
📂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";
}
}
내가 배우는 JAVA란 무엇일까? 자바의 아버지라고 불리는 제임스 고슬링과 그 동료들이 1991년에 그린 프로젝트란 이름으로 시작해서 1995년에 발표된 프로그래밍 언어입니다. Java는 원래 가전제품 내에 동작하는 프로그램을 위해 개발했지만 웹의 등장으로 엄청난 성공을 거두면서 프로그램 개발에 있어 주류 언어중 하나가 되었고, 안드로이드 개발에도 자바가 널리 사용되고 특히 우리 나라 대부분의 공공기관에서 조차 사용하는 프로그램은 언어이기 때문에 활용도가 굉장히 높습니다. < 자바 프로그래밍의 특징 > ● 다양한 환경에서 사용할 수 있다. ● 프로그램이 안정적이다. ● 풍부한 기능이 제공되는 언어이다. ● 객체 지향 언어이다. - Object oriented programming(OOP) 이라고 함 [출처] 자바 소개|작성자 devnote1
자바 개발을 위한 용어 정리
JDK(Java Development Kit) : 자바에서 제공되는 개발용 라이브러리. 계속 버전이 올라가고 있음
JRE(Java Runtime Environment) : 자바 프로그램이 실행되는 환경. 8.0까지 무료로 제공 됨
JVM(Java Virtual Machine) : 자바 가상 머신으로 프로그램이 실행되는 환경인 JRE 가 설치되어 있어야 함