해당 어노테이션이 적용된 클래스는 "Controller"임을 나타내고, bean으로 등록되어 해당 클래스가 Controller로 사용됨을 Spring Framework에 알린다.
@RequestMapping
요청 URL을 어떤 method가 처리할 지 mapping 해주는 어노테이션.
Controller나 Controller의 method에 적용한다.
요청 받는 형식을 정의하지 않으면, 자동으로 GET으로 설정.
ex) "/school" 요청에 대해 공통적으로 처리해야 될 클래스를 의미.
또한, 요청 url에 대해 해당 메소드에서 처리해야 되는 경우에도 사용.
@RequestMapping에 대한 모든 매핑 정보는 Spring에서 제공하는 HandlerMapping Class가 가지고 있다.
@Controller
@RequestMapping(path="/school")
public class SchoolApiController {
@Autowired
private schoolService schoolService;
@GetMapping
public String getSchoolInfo(@RequestParam int schoolId, ModelMap model) {
School shcoolInfo = schoolService.getSchoolInfoBySchoolId(schoolId);
model.addAttribute("schoolInfo", schoolInfo);
return "list";
}
}
@Controller 어노테이션을 통해 해당 클래스가 컨트롤러 클래스임을 spring framework에 전달하고, "/school" 로 들어오는 공통 요청 url 중 get 방식으로 들어오는 url을 처리하는 메소드인 예제.
@RestController
@Controller + @ResponseBody 이며, 메소드의 return 값을 문자열(JSON) 형태로 반환.
Spring의 Controller 중 View로 응답하지 않는 Controller.
API만 지원하는 클래스에 사용되며, json이나 xml과 같은 문자열 return이 주 목적.
@ResponseBody
메소드에 @ResponseBody 어노테이션이 있다면 리턴값이 View를 통해 출력되지 않고, HTTP Response Body에 직접 쓰여지게 된다.
이 때, 쓰여지기 전에 리턴되는 데이터 타입에 따라 MessageConverter에서 변환이 이뤄진 후 쓰여진다.
@RestController
@RequestMapping(path="/school")
public class SchoolApiController {
@Autowired
private SchoolService schoolService;
@PostMapping
public School saveSchoolInfo(@RequestBody School param) {
School saveInfo = schoolService.setSchoolInfo(param);
return saveInfo;
}
}
@Controller 어노테이션이 아닌, @RestController 어노테이션이므로 return을 view 페이지 대신 saveInfo 라는 객체를 JSON 형태로 변환한 데이터를 return한다.
@Controller vs @RestController
@Controller
@RestController
API 사용과 View return을 동시에 하는 경우에 사용. API 서비스로 사용하는 경우는 @ResponseBody를 사용하여 객체 반환
view가 필요없는 API 만 지원시 사용. @RestController = @Controller + @ResponseBody
=> 주석처럼 라우트 레벨에서 코드를 분할한 후 별도의 chunk 파일을 생성하고, 실제 이 라우트를 방문했을 때 리소스를 다시 로드하게 된다. 여기서 chunk 파일은 about이라는 이름으로 생성된다. 컴포넌트 import 시 /* webpackChunkName: "about" */ 라는 주석으로 chunk 파일 이름을 정의했기 때문이다.
첫번째 라우트와 두번째 라우트의 가장 큰 차이는 첫번째는 사용자가 해당 경로에 접근하지 않아도 이미 vue 파일을 import 하는 것이고, 두번째 방법은 path에 접근하기 전까지는 vue 파일에 대한 import 가 일어나지 않는다.
Lazy Load(비동기 컴포넌트)
Vue CLI를 통해 빌드를 진행하면 소스 코드가 하나의 파일로 합쳐지는데, 프로젝트가 클수록 전체 코드가 하나로 합쳐지면서 파일 용량이 커지게 된다.
이로 인해, 사용자가 웹사이트에 처음 접속했을 때 한 번에 큰 파일을 다운로드하느라 초기 랜더링 시간이 길어지게 된다.
페이지가 한번 로드되고 나서는 페이지 전환이 매우 빠르기 때문에 굉장한 이점이 있긴 하다.
하지만 사용하는 페이지가 별로 없다고 한다면, 사용하지도 않을 페이지를 로드하는게 불편해지게 되는 것이다.
Lazy Load는 리소스를 컴포넌트 단위로 분리하여 컴포넌트 혹은 라우터 단위로 필요한 것들만 그때 그때 다운받을 수 있게 하는 방법이다.
Vue CLI3부터 prefetch 기능이 추가가 되었는데, prefetch 기능은 미래에 사용될 수 있는 리소스(about과 같은 비동기 컴포넌트)를 캐시에 저장함으로써 사용자가 접속했을 때 빠르게 리소스를 내려줄 수 있다.
굉장히 유용한 기능이지만, 비동기 컴포넌트로 정의된 모든 리소스를 당장 사용하지 않더라도 캐시에 담아야 하는 단점이 있다.
Vue CLI에서 prefetch 기능은 기본값으로 true이다.
때문에 Lazy Load가 적용된 컴포넌트는 모두 prefetch 기능이 적용되어 캐시에 저장된다.
prefetch 기능을 사용하지 않을 때
요청 수가 훨씬 줄어든다.
라우터가 이동될 때마다 해당 라우터에서 필요한 리소스를 그때 그때 가져온다. (한번 가져온 리소스는 다시 요청하지 않는다.)
prefetch 기능을 사용할 때
요청 수가 많아진다.
애플리케이션의 첫 화면 접속 시 랜더링 속도가 느려질 수 있다.
첫 화면에서 사용되는 리소스는 마지막에 다운받게 되는데, 아직 사용하지 않는 화면의 리소스를 모두 내려받고 첫 화면의 리소스를 다운받기 때문이다.
오히려 초기 랜더링은 prefetch 기능을 사용하지 않아야 로딩이 빠르다.
prefetch 기능은 다른 화면에서 사용할 리소스를 미리 내려 받아, 빠른 화면 전환을 위해 사용되기 때문에..
"정말 필요한 컴포넌트에 대해 prefetch 기능을 적용하는 것이 좋다."
Vue CLI에서 prefetch 기능이 디폴트로 true이기 때문에 prefetch 기능을 끄는 법을 알아보자.
vue-project/blob/master/ 경로에 Vue.config.js 파일을 생성하고 다음 코드를 추가한다.
prefetch 기능을 삭제해도, 비동기 컴포넌트인 Lazy Load로 컴포넌트를 사용할 수 있다.
패키지를 설치할 때 사용가능한 옵션이 여러 가지 있지만, -g와 --save에 대해서만 짚고 넘어간다.
npm install -g 패키지명
-g(global) 옵션은 설치하는 패키지가 현재 디렉토리뿐만 아니라 앞으로 생성하게 되는 모든 프로젝트에서 사용할 수 있는 global 패키지로 등록된다.
npm install 패키지명 --save
현재 작업중인 디렉토리 내에 있는 ./node_modules에 패키지를 설치한다.
그 다음에 package.json 파일에 있는 dependencies 객체에 지금 설치한 패키지 정보를 추가한다.
설치되는 모든 패키지는 node_modules 디렉토리에 설치된다.
node_modules 디렉토리에서 현재 사용하고 있는 모든 패키지를 확인할 수 있다.
--save 옵션을 사용하면 package.json 파일에 설치한 패키지 정보가 추가되기 때문에
package.json 파일을 팀원들에게 공유하면 명령어 npm install을 통해 현재 내 프로젝트 디렉토리에 없는 패키지 전체를 한번에 설치할 수 있다.
vue create vue-project
디렉토리명을 vue-project 로 하여 프로젝트를 설치한다.
Vue 3 버전을 사용할 것이므로 아래의 Vue 3을 선택하여 설치를 진행한다.
그렇게 되면 왼쪽 패널에 Vue 프로젝트를 생성하면서 vue-project 의 이름으로 폴더가 생성되고,
프로젝트 폴더 하위로 Vue 프로젝트 개발을 위한 기본적인 파일들이 자동으로 생성된다.
Vue 프로젝트 실행
프로젝트 생성이 끝나면 터미널에 표시된 가이드대로 명령어를 입력하여 프로젝트를 실행해본다.
cd vue-project
프로젝트 폴더로 이동
npm run serve
서버가 시작되며, 프로젝트가 기본 포트 8080으롷 실행된다.
- 기본 포트 이외의 포트를 사용하려면 npm run serve -- --port 포트번호 명령어로 직접 포트번호를 지정할 수 있다.
ex) npm run serve -- --port 3000
크롬을 켜서 확인을 해보고, 아래 화면이 나타나면 Vue 프로젝트가 정상적으로 설치된 것이다.
Vue 프로젝트 실행 화면
Vue 프로젝트 파일 구조
설치된 프로젝트에 어떤 파일들이 생성되었는지 기본적인 구조를 살펴보자
Vue 프로젝트 파일 구조
node_modules
npm으로 설치된 패키지 파일이 모여 있는 디렉토리
public
웹팩(webpack)을 통해 관리되지 않는 정적 리소스가 모여 있는 디렉토리
src/assets
이미지, css, 폰트 등을 관리하는 디렉토리
src/components
Vue 컴포넌트 파일이 모여 있는 디렉토리
App.vue
최상위(root) 디렉토리
main.js
가장 먼저 실행되는 자바스크립트 파일로써, Vue 인스턴스를 생성하는 역할
.gitignore
깃허브에 업로드할 때 제외할 파일 설정
babel.config.js
바벨(Babel) 설정 파일
package-lock.json
설치된 package의 dependency 정보를 관리하는 파일
package.json
프로젝트에 필요한 package를 정의하고 관리하는 파일
README.md
프로젝트 정보를 기록하는 파일
package.json 파일 구성
name
프로젝트 이름을 입력
version
프로젝트의 버전 정보를 입력
private
이 옵션을 true로 설정 시, 해당 프로젝트를 npm으로 올릴 수 없다. 개발자가 실수로 해당 프로젝트를 npm에 올리더라도 이 옵션이 true로 되어 있으면 배포를 막을 수 있다.
scripts
프로젝트 실행과 관련된 명령어들을 등록한다. 프로젝트 실행, 빌드 등과 같이 프로젝트 개발, 운영 시 사용되는 다양한 scripts 명령어를 등록하고, 쉽게 사용할 수 있다. 개발자가 직접 정의한 script는 npm run 명령어로 사용하고, npm에서 제공되는 명령어는 npm 명령어로 사용한다.
dependencies
사용중인 패키지 정보를 입력한다.
devDependencies
프로젝트 배포 시 필요없는, 개발 시에만 필요한 패키지 정보가 등록되는 곳
eslintConfig
ESLint는 일관성 있게 코드를 작성하고 버그를 식별하고 회피할 목적으로 ECMAScript/Javascript 코드에서 발견된 패턴을 개발자에게 알려주는 플러그인이다. 구문 분석을 위해 babel-eslint를 파서로 사용
browserslist
전 세계 사용 통계 속에서 상위 1% 이상 사용된 브라우저, 각 브라우저의 최신 버전 2개를 지원하도록 한다.
패키지를 설치하면 dependencies, devDependencies, eslintConfig 등은 자동으로 채워지며,
컬렉션 타입을 반환, 연관 배열은 반환하지 못하고 중첩 테이블이나 VARRAY만 반환 가능
기본 함수는 하나의 값만 반환이 가능하지만 테이블 함수로는 여러 행, 열의 반환이 가능
Oracle Table Function은 Multi Column + Multi Row로 값을 return할 수 있는 PL/SQL Function
문법
일반 함수와 다를 건 없지만, 대상이 되는 컬렉션 타입이 먼저 만들어져 있어야 함.
다음 예시는 DEPT와 EMP 테이블을 예를 들어, DEPT 번호를 파라미터로 전달하여 부서 및 직원 정보를 가져오는 쿼리를 TABLE 함수 형태로 변환
OBJECT 타입 생성
행을 리턴받는 역할
함수에서 반환하는 레코드의 스키마
(임시) TABLE TYPE 객체 생성
함수에서 반환하는 레코드의 집합(테이블) 정의
Table Function 생성 (테이블 Return)
"Table" 키워드를 사용하여 조회
예시
-- 1. OBJECT 타입 생성
CREATE OR REPLACE TYPE DEPT_TYPE AS OBJECT (
DEPTNO NUMBER(2),
DNAME VARCHAR2(14),
LOC VARCHAR2(13),
EMPNO NUMBER(4),
ENAME VARCHAR2(10)
)
-- 2. TABLE 타입 생성
CREATE OR REPLACE TYPE DEPT_TABLE AS TABLE OF DEPT_TYPE;
-- 3. 함수 생성 (테이블 리턴)
CREATE OR REPLACE FUNCTION GET_DEPT_FN(P_DEPT_NO IN NUMBER)
RETURN DEPT_TABLE
IS
V_RSLT DEPT_TABLE;
BEGIN
SELECT DEPT_TYPE(A.DEPTNO, A.DNAME, A.LOC, B.EMPNO, B.ENAME)
BULK COLLECT INTO V_RSLT
FROM DEPT A , EMP B
WHERE A.DEPTNO = B.DEPTNO
AND A.DEPTNO = P_DEPT_NO;
RETURN V_RSLT;
END GET_DEPT_FN;
-- 실행예시
SELECT * FROM TABLE(GET_DEPT_FN(30));
Pipelined Table Function
Table Function과 거의 유사, 한 행 단위로 바로바로 즉시 값을 리턴하는 함수
한 Row씩 처리하므로 결과값들이 바로 출력되기 시작
Oracle 9i부터 사용
OBJECT 타입 생성
(임시) TABLE TYPE 객체 생성
Pipelined Table Function 생성
조회
-- 1. OBJECT 타입 생성
CREATE OR REPLACE TYPE DEPT_TYPE AS OBJECT (
DEPTNO NUMBER(2),
DNAME VARCHAR2(14),
LOC VARCHAR2(13),
EMPNO NUMBER(4),
ENAME VARCHAR2(10)
)
-- 2. TABLE 타입 생성
CREATE OR REPLACE TYPE DEPT_TABLE AS TABLE OF DEPT_TYPE;
-- 3. Pipelined 함수 생성 (테이블 리턴)
CREATE OR REPLACE FUNCTION GODDAEHEE.GET_DEPT_PIPE_FN(P_DEPT_NO IN NUMBER)
RETURN DEPT_TABLE
PIPELINED
IS
V_RSLT DEPT_TYPE;
BEGIN
FOR DEPT_CUR IN (
SELECT A.DEPTNO, A.DNAME, A.LOC, B.EMPNO, B.ENAME
FROM DEPT A
, EMP B
WHERE A.DEPTNO = B.DEPTNO
AND A.DEPTNO = P_DEPT_NO
)
LOOP
V_RSLT := DEPT_TYPE(DEPT_CUR.DEPTNO, DEPT_CUR.DNAME, DEPT_CUR.LOC, DEPT_CUR.EMPNO, DEPT_CUR.ENAME);
PIPE ROW(V_RSLT);
END LOOP;
RETURN;
END;
-- 실행예시
SELECT * FROM TABLE(GET_DEPT_PIPE_FN(30));
OR_REPLACE : 함수생성 DDL 명령어 / 이미 존재하는 함수라면 기존 내용 지우고 재생성, 생략 가능
function_name : 사용자 지정 함수명
argument : 매개변수1, 매개변수2, 매개변수3 ... 매개변수 선언
datatype : VARCHAR2, NUMBER, DATE 등 반환할 데이터 타입 지정
함수 작성 및 사용 예시
함수 작성 예시
CREATE OR REPLACE FUNCTION FN_GET_DEPT_NAME(
P_DEPT_NO IN VALCHAR2
) RETURN VARCHAR2
IS
V_TEST_NAME VARCHAR2(10);
BEGIN
SELECT DELP_NAME
INTO V_TEST_NAME
FROM DEPT
WHERE DEPT_NO = P_DEPT_NO;
RETURN V_TEST_NAME;
END;
CREATE OR REPLACE PROCEDURE 프로시저이름
IS
[
변수이름 데이터타입; -- 프로시저내에서 사용할 변수선언
변수이름 데이터타입;
변수이름 데이터타입;
..
]
BEGIN
기능 구현;
END;
EX) 인수없는 프로시저
SET SERVEROUTPUT ON;
CREATE OR REPLACE PROCEDURE p_test
IS
I_MESSAGE VARCHAR2(100) := 'https://pdache.tistory.com';
BEGIN
dbms_output.put_line(I_MESSAGE);
END;
EXEC p_test; --프로시저 호출
(인수 있는) 프로시저 생성 방법
CREATE OR REPLACE PROCEDURE 프로시저이름(
변수이름 IN 데이터타입, 변수이름 IN 데이터타입, .... --IN 생략가능
)
IS
[
변수이름 데이터타입; -- 프로시저내에서 사용할 변수선언
..
]
BEGIN
기능 구현;
END;
EX) 인수 있는 프로시저
CREATE OR REPLACE PROCEDURE p2_test( p_name IN VARCHAR2 )
IS
BEGIN
dbms_output.put_line(p_name||' 님 안녕하세요?');
END;
EXEC p2_test; --프로시저 호출 (오류발생 )
-- (프로시저에 in이 있으면서 기본값이 없기 때문에 반드시 인수값 필요함)
EXEC p2_test('Pdache');
※인수의 타입 선언 부분 정리
IN => 내부 프로그램에 제공
변수이름 IN VARCHAR2; -- 인수선언할 때 byte 수 지정안함(인수는 크기를 주지 않음)
변수이름 IN 테이블이름.컬럼명%TYPE;
변수이름 IN 테이블이름.컬럼명%TYPE:=값;
변수이름 IN 테이블이름.컬럼명%TYPE DEFAULT 값;
OUT => 호출자에게 제공
프로시저 실행 시점에 OUT 매개변수를 변수 형태로 전달하고, 프로시저 실행부에서 이 매개변수에 특정 값을 할당
변수이름 IN VARCHAR2;
변수이름 IN 테이블이름.컬럼명%TYPE;
IN OUT => 입력과 동시에 출력용으로 사용할 수 있다.
변수이름 IN VARCHAR2;
변수이름 IN 테이블이름.컬럼명%TYPE;
※ IN OUT, OUT 모드로 선언된 파라미터에는 DEFAULT를 적용할 수 없다.
프로시저 호출 방법
EXEC 프로시저이름; -- 인수없는 경우 호출
EXEC 프로시저이름(값, 값, ...); - 인수있는 경우 호출
저장된 프로시저 찾기
작성된 프로시저를 찾기 위해 데이터 사전을 이용할 수 있다.
데이터 사전은 대문자로 값을 저장하기 때문에 대문자로 검색한다.
SELECT * FROM user_objects WHERE object_type = 'PROCEDURE';
SELECT * FROM user_source WHERE name = '프로시저명';
프로시저 output 매개변수 사용하기
웹 개발을 하면서 프로시저 결과 핸들링을 위해 많이 사용
프로시저를 실행하여 특정 결과 값을 OUT 변수에 저장하여 보냄
OUT 있는 프로시저 작성방법
CREATE OR REPLACE PROCEDURE 프로시저이름 (
변수이름 IN 데이터타입, 변수이름 IN 데이터타입, .... --in 생략가능
변수이름 OUT 데이터타입, 변수이름 OUT 데이터타입 ... --프로시저를 호출하는곳으로 값을 보낸다.
)
IS
[
변수이름 데이터타입; -- 프로시저내에서 사용할 변수선언
..
]
BEGIN
기능 구현;
END;
EX) 이름을 OUTPUT 해주는 프로시저
CREATE OR REPLACE PROCEDURE p_outTest(
p_NAME OUT VARCHAR2
)
IS
BEGIN
p_NAME := 'Pdache';
DBMS_OUTPUT.PUT_LINE('호출 완료');
END;
--out이 있는 프로시저 호출방법
--DECLARE 로 선언되변수는 일회용
DECLARE
OUT_MSG VARCHAR2(2000);
BEGIN
p_outTest(OUT_MSG); -- 프로시저를 실행 한후에 out을 받을 변수지정
dbms_output.put_line(OUT_MSG); -- 출력하기
END;
오라클 DB에서 실행되는 모든 SQL 문장은 암시적 커서가 생성되며, 커서 속성을 사용할 수 있다.
모든 DML과 PL/SQL SELECT문에 대해 선언
암시적 커서는 오라클이나 PL/SQL 실행 메커니즘에 의해 처리되는 SQL 문장이 처리되는 곳에 대한 익명의 주소
Oracle 서버에서 SQL문을 처리하기 위해 내부적으로 생성하고 관리
암시적 커서는 SQL문이 실행되는 순간 자동으로 OPEN, CLOSE 실행
SQL 커서 속성을 사용하면 SQL문 결과 테스트 가능
속성
SQL%FOUND : 해당 SQL문에 의해 반환된 총 행수가 1개 이상일 경우 TRUE(BOOLEAN)
SQL%NOTFOUND : 해당 SQL문에 의해 반환된 총 행수가 없을 경우 TRUE(BOOLEAN)
SQL%ISOPEN : 항상 FALSE, 암시적 커서가 열려 있는지의 여부 검색(PL/SQL은 실행 후 바로 CLOSE)
SQL%ROWCOUNT : 해당 SQL문에 의해 반환된 총 행수, 가장 최근 수행된 SQL문에 의해 영향을 받은 행의 갯수(정수)
EX)
SET SERVEROUTPUT ON;
DECLARE
BEGIN
DELETE FROM emp WHERE DEPTNO = 10;
DBMS_OUTPUT.PUT_LINE('처리 건수 : ' || TO_CHAR(SQL%ROWCOUNT)|| '건');
END;
-- 결과
처리 건수 : 21건
PL/SQL 처리가 정상적으로 완료되었습니다.
명시적 커서(Explicit Cursor)
정의
프로그래머에 의해 선언되어 이름이 있는 커서
속성
%ROWCOUNT : 현재까지 반환된 모든 행의 수를 출력
%FOUND : FETCH한 데이터가 행을 반환하면 TRUE
%NOTFOUND : FETCH한 데이터가 행을 반환하지 않으면 TRUE(LOOP를 종료할 시점을 찾을 때 사용)
%ISOPEN : 커서가 OPEN 되어 있으면 TRUE
문법
DECLARE를 통해서 명명된 SQL 영역 생성
OPEN을 이용하여 결과 행 집합 식별
FETCH를 통해 현재 행을 변수에 로드(현재 행이 없을 때까지 수행 가능)
CLOSE를 통해 결과 행 집합 해제
DECLARE
CURSOR [커서이름] IS [SELECT 구문];
BEGIN
OPEN [커서이름];
FETCH [커서이름] INTO [로컬변수];
CLOSE [커서이름];
END;
OPEN(커서 열기)
OPEN 문을 사용하여 커서를 연다.
커서 안의 검색이 실행되며 아무런 데이터 행을 추출하지 못해도 에러가 발생하지 않는다.
FETCH(커서 페치)
현재 데이터 행을 OUTPUT 변수에 반환
커서의 SELECT 문의 컬럼 수와 OUTPUT 변수의 수가 동일해야 한다.
커서 컬럼의 변수 타입과 OUTPUT 변수의 데이터 타입 역시 동일해야 한다.
커서는 한 라인씩 데이터를 FETCH
문법 : FETCH cursor_name INTO variable1, variable2;
CLOSE(커서 닫기)
사용을 마친 커서는 반드시 닫아준다.
필요시 커서는 다시 열 수 있다.
커서를 닫은 상태에서 FETCH는 불가능하다.
문법 : CLOSE cursor_name;
EX 1) 커서 사용 예제 1
SET SERVEROUTPUT ON;
DECLARE
CURSOR emp_cur -- 커서정의
IS
SELECT * FROM emp WHERE DEPTNO = 10;
emp_rec emp%ROWTYPE; -- 변수정의
BEGIN
OPEN emp_cur;
LOOP -- 반복
FETCH emp_cur INTO emp_rec; -- 하나씩 변수에 넣기
EXIT WHEN emp_cur%NOTFOUND; -- 더이상 없으면 끝내기
DMBS_OUTPUT.PUT_LINE(emp_rec.empno || ' ' || emp_rec.name); -- 출력
END LOOP;
CLOSE emp_cur;
END;
EX 2) 커서 사용 예제 2
SET SERVEROUTPUT ON;
DECLARE
ID_LIST SYS_REFCURSOR; -- 커서정의
I_ID VARCHAR2(100); -- 변수정의
BEGIN
OPEN ID_LIST;
FOR
SELECT USER_ID FROM MY_USER WHERE 조건;
LOOP -- 반복
FETCH ID_LIST INTO I_ID; -- 하나씩 변수에 넣기
EXIT WHEN ID_LIST%NOTFOUND; -- 더이상 없으면 끝내기
DMBS_OUTPUT.PUT_LINE(I_ID); -- 출력
END LOOP;
CLOSE ID_LIST;
END;
EX 3) 커서 사용 예제 3
SET SERVEROUTPUT ON;
DECLARE
CURSOR ID_LIST IS
SELECT 'GOD' AS USER_ID FROM DUAL;
BEGIN
FOR TEST_CURSOR IN ID_LIST
LOOP
DBMS_OUTPUT.putline(TEST_CURSOR.USER_ID);
END LOOP;
END;
명시적 커서 FOR LOOP
서브 쿼리를 활용하여 CURSOR FOR LOOP를 사용하면 CURSOR를 선언할 필요도 없어진다.
명시적 커서 FOR LOOP를 사용하면 FOR LOOP가 자동적으로 커서를 OPEN, 행이 없을 때까지 FETCH하고, CLOSE
ROWTYPE에 해당하는 변수를 따로 DECLARE할 필요가 없다. - 암시적으로 선언되기 때문
이 암시적 카운터는 FOR LOOP에서만 사용 가능
이 커서의 데이터 타입(컬럽 데이터 타입의 집합)도 %ROWTYPE 앞으로 이용 가능
EX 3) 커서 사용 예제 3을 "명시적 커서 FOR LOOP" 방식으로 변경한 예
SET SERVEROUTPUT ON;
DECLARE
BEGIN
FOR ID_LIST IN
(
SELECT 'GOD' AS USER_ID FROM DUAL
)
LOOP
DBMS_OUTPUT.putline(ID_LIST.USER_ID);
END LOOP;
END;
※ CURSOR FOR LOOP은 내부적으로 처리되는 데이터의 양, I/O 측면에서 훨씬 효율적, 가급적 사용 권장
FOR index in [REVERSE] 시작값 .. END값 LOOP
STATEMENT 1
STATEMENT 2
...
END LOOP;
index는 자동 선언되는 binary_integer형 변수이고, 1씩 증가
REVERSE 옵션이 사용될 경우 index는 upper_bound에서 lower_bound로 1씩 감소
IN 다음에는 cursor나 SELECT 문이 올 수 있다.
EX 1)
SET serveroutput ON;
BEGIN
FOR i in 1..4 LOOP
if mod(i, 2) = 0 then
dbms_output.put_line( i || '는 짝수!!');
else
dbms_output.put_line( i || '는 홀수!!');
end if;
END LOOP;
END;
EX 2) 간단한 FOR문 안에 SELECT문이 오는 예제
SET serveroutput ON;
BEGIN
FOR NUM_LIST IN
(
SELECT 1 AS NUM FROM DUAL
UNION ALL
SELECT 2 AS NUM FROM DUAL
UNION ALL
SELECT 3 AS NUM FROM DUAL
UNION ALL
SELECT 4 AS NUM FROM DUAL
)
LOOP
if mod(NUM_LIST.NUM, 2) = 0 then
dbms_output.put_line( NUM_LIST.NUM || '는 짝수!!');
else
dbms_output.put_line( NUM_LIST.NUM || '는 홀수!!');
end if;
END LOOP;
END;
LOOP
문법
LOOP
PL/SQL STATEMENT 1
다른 LOOP를 포함하여 중첩으로 사용 가능
EXIT [WHEN CONDITION]
END LOOP;
EXIT 문이 사용되었을 경우, 무조건 LOOP문을 빠져나간다.
EXIT WHEN 조건이 사용될 경우, WHEN절에서 LOOP를 빠져나가는 조건을 제어할 수 있다.
EX 1) 기본 LOOP문 예제
SET serveroutput ON;
DECLARE
v_num NUMBER := 6; -- 시작숫자
v_tot_num NUMBER := 0; -- 총 loop수 반환 변수
BEGIN
LOOP
DBMS_OUTPUT.PUT_LINE('현재 숫자 : ' || v_num);
v_num := v_num + 1;
v_tot_num := v_tot_num + 1;
EXIT WHEN v_num > 10;
END LOOP;
DBMS_OUTPUT.PUT_LINE(v_tot_num || '번의 LOOP');
END;
EX 2) WHILE LOOP문 예제
SET serveroutput ON;
DECLARE
v_num NUMBER := 6; -- 시작숫자
v_tot_num NUMBER := 0; -- 총 loop수 반환 변수
BEGIN
WHILE v_num < 11 LOOP
DBMS_OUTPUT.PUT_LINE('현재 숫자 : ' || v_num);
v_num := v_num + 1;
v_tot_num := v_tot_num + 1;
-- EXIT WHEN v_num > 10;
END LOOP;
DBMS_OUTPUT.PUT_LINE(v_tot_num || '번의 LOOP');
END;
제어문
IF문, CASE문 등의 제어문을 PL/SQL에서도 사용할 수 있다.
IF
문법
IF 조건1 THEN
처리문1
ELSE IF 조건2 THEN
처리문2
...
ELSE
처리문
END IF;
IF문은 조건값이 참이면 해당 조건의 처리 문장이 실행
다른 프로그래밍 IF문과 다른 점
조건 다음에 THEN을 붙인다.
보통 ELSE IF를 쓰지만, PL/SQL에서는 ELSIF 명령어를 사용한다.
마지막에는 IF를 끝낸다는 END IF를 붙인다.
EX 1) 기본 IF문 예제
DECLARE
v_score NUMBER := 79;
BEGIN
IF v_score >= 90 THEN
DBMS_OUTPUT.PUT_LINE('점수 : ' || v_score || ', 등급 : A');
ELSIF v_score >= 80 THEN
DBMS_OUTPUT.PUT_LINE('점수 : ' || v_score || ', 등급 : B');
ELSIF v_score >= 70 THEN
DBMS_OUTPUT.PUT_LINE('점수 : ' || v_score || ', 등급 : C');
ELSIF v_score >= 60 THEN
DBMS_OUTPUT.PUT_LINE('점수 : ' || v_score || ', 등급 : D');
ELSE
DBMS_OUTPUT.PUT_LINE('점수 : ' || v_score || ', 등급 : F');
END IF;
END;
EX 2) IF문 예제 2 - 조건이 NULL인 경우
NULL의 비교는 항상 NULL이기 때문에, ELSE 이하 문만 수행
DECLARE
v_score NUMBER;
BEGIN
IF v_score >= 90 THEN
DBMS_OUTPUT.PUT_LINE('점수 : ' || v_score || ', 등급 : A');
ELSIF v_score >= 80 THEN
DBMS_OUTPUT.PUT_LINE('점수 : ' || v_score || ', 등급 : B');
ELSIF v_score >= 70 THEN
DBMS_OUTPUT.PUT_LINE('점수 : ' || v_score || ', 등급 : C');
ELSIF v_score >= 60 THEN
DBMS_OUTPUT.PUT_LINE('점수 : ' || v_score || ', 등급 : D');
ELSE
DBMS_OUTPUT.PUT_LINE('점수 : ' || v_score || ', 등급 : F');
END IF;
END;
CASE문
문법
오라클 CASE문과 유사
CASE WHEN 조건1 THEN
처리문1
WHEN 조건2 THEN
처리문2
...
ELSE
처리문
END CASE;
EX 1) CASE 표현식 예제
단순하게 조건문에 따른 값을 대입할 때 사용
DECLARE
v_grade CHAR(1) := 'C';
v_appraisal VARCHAR2(20) ;
BEGIN
v_appraisal := CASE v_grade
WHEN 'A' THEN 'Excellent'
WHEN 'B' THEN 'Very Good'
WHEN 'C' THEN 'Good'
ELSE 'No such grade'
END;
DBMS_OUTPUT.PUT_LINE ('Grade : '|| v_grade) ;
DBMS_OUTPUT.PUT_LINE ('Appraisal: '|| v_appraisal);
END;
EX 2) CASE 조건문 예제
단순한 값이 아닌 PL/SQL문(명령문)을 실행하고자 하는 경우에 CASE 조건문 사용
CASE 조건문은 끝이 END CASE로 끝남
DECLARE
v_grade CHAR(1) := 'C';
v_appraisal VARCHAR2(20) ;
BEGIN
CASE v_grade
WHEN 'A' THEN
v_appraisal := 'Excellent';
WHEN 'B' THEN
v_appraisal := 'Very Good';
WHEN 'C' THEN
v_appraisal := 'Good';
ELSE
v_appraisal := 'No such grade';
END CASE;
DBMS_OUTPUT.PUT_LINE ('Grade : '|| v_grade) ;
DBMS_OUTPUT.PUT_LINE ('Appraisal: '|| v_appraisal);
END;