Now Loading ...
-
리액트 시작하기 (6 ~ 장)
State
리액트 Component의 상태를 관리하는 객체
state는 개발자가 정의한다.
랜더링이나 데이터의 흐름에 사용되는 값들만 state에 포함시켜야한다.
state가 변경될 경우, 컴포넌트가 재 랜더링 되기 때문에 랜더링과 데이터의 흐름에 관계없는 데이터를 저장하면, 컴포넌트가 재 랜더링되어 성능에 영향을 줄 수 있다.
컴포넌트의 인스턴스 필드로 정의
Javascript 객체라 생각
class LikeButton extends React.Component {
constructor(props) {
super(props);
this.state = {liked: false}; // state 정의
}
}
state는 직접 수정할 수 없다.
(X)
this.state = {name : 'Inje'}
this.setState({
name: 'Inje'
});
컴포넌트가 계속 존재하는게 아니라 시간에 따라 생성, 업데이트되다가 사라진다.
Hooks
함수 컨퍼넌트도 class 컴포넌트처럼 state를 가질 수 있게 해주는 기능
갈고리
함수명 앞에 use가 붙어서 훅이라는 것을 명시해준다.
useState Hook
state 를 사용하기 위한 Hook
const [변수명, set함수명] = useState(초기값);
// return 값은 배열 -> 1. state 변수, 2. state를 업데이트하는(set) 함수
import React, {useState} from "react";
function Counter(props) {
const [count, setCount] = useState(0); // count라는 state 변수와 setCount라는 업데이트 함수 생성
// setCount -> 변수 각가에 대해 set함수가 따로 존재!
return (
<div>
<p>총 {count}번 클릭했습니다.</p>
<button onClick={() => setCount(count + 1)}>
클릭
</button>
</div>
);
}
useEffect()
side effect를 수행하기 위한 Hook
side effect = 효과, 영향
다른 컴포넌트에 영향을 미칠 수 있으며, 렌더링 중에는 작업이 완료될 수 없기 때문이다.
함수 컴포넌트에서 Side effect를 실행할 수 있게 해주는 Hook
useEffect(이펙트 함수, 의존성 배열);
첫번째 컴포넌트가 랜더링 된 이후, 재 랜더링 된 이후에 실행됨
useEffect(이펙트 함수);
의존성 배열 생략 시, 컴포넌트가 업데이트 될 때마다 호출됨
useEffect와 useState를 함께 사용하여 컴포넌트의 상태를 관리할 수 있다.
import React, {useState, useEffect} from "react";
function Counter(props) {
const [count, setCount] = useState(0);
// componentDidMount, componentDidUpdate와 비슷하게 작동한다
useEffect(() => {
// 브라우저 API를 사용해서 document의 title을 업데이트
document.title = `You clicked ${count} times`;
});
return (
<div>
<p>총 {count}번 클릭했습니다.</p>
<button onClick={() => setCount(count + 1)}>
클릭
</button>
</div>
);
}
import React, {useState, useEffect} from "react";
function UserStatus(pros) {
const [isOnline, setIsOnline] = useState(null);
function handleStatusChange(status) {
seInOnline(status.isOnline);
}
useEffect( () => {
ServerAPI.subscribeUserStatus(props.userId, handleStatusChange);
return () => {
ServerAPI.unsubscribeUserStatus(props.user.id, handleStatusChange);
};
});
if(isOnline === null) {
return '대기 중...';
}
return isOnline ? '온라인' : '오프라인';
}
function UserStatusWithCounter(props) {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `총 ${count}번 클릭했습니다.`;
});
const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
ServerAPI.subscribeUserStatus(props.userId, handleStatusChange);)
return () => {
ServerAPI.unsubscribeUserStatus(props.userId, handleStatusChange);
};
});
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
}
useMemo, useCallback, useRef
useMemo
Memoized value를 리턴하는 Hook
Memoization : 최적화를 사용하는 개념, 연산 값이 많은 값을 저장해놨다가 이전에 사용한 결과를 받아옴
사용법
const memoizedValue = useMemo( () => {
// 연산값이 높은 작업을 수행하여 결과를 반환
return computeExpensiveValue(의존성 변수1, 의존성 변수2);
},
[의존성 변수1, 의존성 변수2]
);
랜더링에서 일어날 내용을 넣으면 안됨 ex) useEffect에서 실행되어야 할 side effect
의존성 배열을 넣지 않으면 컴포넌트가 랜더링 될 때마다 실행된다.
const memoizedValue = useMemo(
() => computeExpensiveValue(a,b)
);
의존성배열이 빈 배열인 경우, 컴포넌트 mount 시에만 호출된다. -> mount 이후에는 변경되지 않음
const memoizedValue = useMemo(
() => computeExpensiveValue(a,b),
[]
);
useMemoHook에 의존성 변수를 넣고, 해당 변수의 값이 변해야 할 경우에만 사용한다.
useCallback()
값이 아닌 함수를 반환
const memoizedCallback = useCallback(
() => {
doSomething(의존성 변수1, 의존성 변수2);
},
[의존성 변수1, 의존성 변수2]
);
따라서 useCallback 과 useMemo 의 함수 선언은 같은 말이다.
useCallback(함수, 의존성 배열); // 위아래 둘다 같은 내용
useMemo(() => 함수, 의존성 배열); // 위아래 둘다 같은 내용
useRef()
reference를 사용하기 위한 Hook -> 특정 컴포넌트를 접근할 수 있는 객체
refObject.current 속성 -> ref 하는 엘리먼트
const refContainer = useRef(null);
컴포넌트가 mount 되기 전까지 계속 유지된다.
function TextInputWithFocusButton() {
const inputEl = useRef(null);
function onButtonClick() {
// `current`가 가리키는 DOM 노드에 포커스를 맞춘다.
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>
Focus the input
</button>
</>
);
}
내부의 데이터가 변경되었을 때, 별도로 알려주지 않는다.
useCallback은 자식 엘리먼트가 변경되었을 때, 알림을 받을 수 있다.
Callback Ref
const mesureRef = useCallback(node => {
if(node !== null) {
// node가 변경되었을 때, 알림을 받는다.
console.log('엘리먼트가 변경되었습니다.', node);
}
}, []);
Hook의 규칙과 Custom Hook 만들기
Hook은 컴포넌트의 최상위 레벨에서만 호출해야 한다.
Hook은 컴포넌트가 렌더링 될 때마다 매번 같은 순서로 호출되어야 한다.
잘못된 Hook 사용법
function MyComponent(props) {
const [name, setName] = useState('Inje');
if(name !== ''){
useEffect( () => {
... // 이 부분은 잘못된 사용 예시입니다.
});
}
}
리액트 함수 컴포넌트에서만 Hook을 호출해야 한다.
일반적인 javascript에서 호출 X
eslint-plugin-react-hooks를 사용하여 Hook의 규칙을 검사할 수 있다.
Custom Hook
Hook을 재사용할 수 있는 방법
Custom Hook 추출하기
이름이 use 로 시작하고, 내부에서 다른 Hook을 호출하는 하나의 자바스크립트 함수
```jsx
// useUserStatus 를 사용하여 사용자 상태를 관리하는 Custom Hook
function UserStatus(props) {
const isOnline = useUserStatus(props.user.id);
if(isOnline === null){
return ‘대기 중…’;
}
return isOnline ? ‘온라인’ : ‘오프라인’;
}
function UserListItem(props) {
const isOnline = useUserStatus(props.user.id);
return (
<li>
{props.user.name} - {isOnline ? '온라인' : '오프라인'}
</li>
); } ``` - 여러개의 컴포넌트에서 하나의 Custom Hook을 사용할 때, 컴포넌트 내부에 있는 모든 state와 effect는 전부 분리되어 있다. - Custom Hook은 컴포넌트의 상태를 공유하지 않는다. (분리된 state와 effect를 가진다.) - 분리된 데이터들을 공유하고싶을 때, ```jsx function CheckUserStatus(props) {
/ ** Custom Hook을 사용하여 사용자 상태를 관리하는 예시
const [userId, setUserId] = useState(1);
const isUserOnline = useUserStatus(userId);
/ ** 으로 하면, 이전에 선택된 사용자를 취소하고, 새로운 사람의 상태를 다시 구독한다. } ```
-
-
-
RDS 란
RDS 란
관계형 데이터 베이스 서비스
RDBS
Amazon Aurora
서버리스 및 머신러닝 어플리케이션 구축을 위해 요구되는 고성능 관계형 DB
데이터 웨어하우스
데이터 관리하는 시스템 유형
데이터 분석 작업 진행
즉, ‘데이터를 보관하고 사용하는 저장 창고’
하나 혹은 여러 개의 소스로부터 데이터 수집, 보관, 규모가 큰 데이터를 분석하는 데 사용
OLTP vs OLAP
OLTP (Online Transaction Processing): 데이터베이스에 삽입되자마자 바로 쿼리하여 사용될 때, 작은 규모의 데이터 불러올 때 사용되는 시스템
OLAP (Online Analytical Processing): 대량의 데이터를 분석하고, 복잡한 쿼리를 실행하는 시스템
데이터베이스 백업
작동 백업 (AB : Auto Backup)
7일에서 35일 설정, 스냅샷과 트랜잭션 로그 생성
(RDS 백업 크기 == S3 백업 크기) = 무료
백업중 I/O 작업 suspension
RDS 인스턴스 삭제 시, 백업 정보 모두 삭제
스냅샷 (Snapshot)
수동으로 생성, 백업 크기만큼 S3에 저장
개발자가 수동으로 실행
백업 원리
원본 인스턴스 백업 시, 새로운 인스턴스 생성
원본 인스턴스 (original) 이름으로 시작, 백업 인스턴스는 (restored) 이름으로 시작
다중 가용 영역과 읽기 전용
다중 가용 영역 (Multi-AZ)
데이터베이스에서 어떤 이벤트 발생 시, 원래 데이터베이스 인스턴스에 업데이트 하는 동시, 존재하는 가용 영역에 복제본이 생성됨
복제본 생성이 실패할 경우, RDS가 자동으로 다른 안정적 가용 영역에 복제본 다시 생성
재해복구
읽기 전용 (Read Replica)
읽기 전용 인스턴스 생성, 원본 인스턴스에 대한 읽기 작업을 분산시켜 성능 향상
복제본 개수는 최대 5개
캐시로 성능 향상(엘라스틱캐시)
AWS 리소스, 클라우드 내 인메모리를 사용해 데이터베이스 성능 향상
종류 : 맴캐시드, 레디스
맴캐시드
오브젝트 캐시 라고 불리며, 메모리 사용량에 있어 문자열 자료형을 처리할 때 주로 사용
캐시의 몸집이 커졌다 작아졌다 함
레디스
복잡한 데이터 타입 메모리 저장
리스트와 해시 테이블로 정렬 (수많은 데이터 정렬에 필요한 비용, 시간을 줄여줌)
다중 가용 영역 기능 포함
-
MCP 란
MCP 란
Model Context Protocol (MCP)는 LLM(대형 언어 모델)과의 상호작용을 위한 프로토콜입니다. MCP는 LLM이 특정 작업을 수행하기 위해 필요한 컨텍스트를 제공하는 데 중점을 둡니다. 이 프로토콜은 LLM이 더 나은 응답을 생성할 수 있도록 돕기 위해 설계되었습니다.
MCP는 LLM이 작업을 수행하는 데 필요한 정보를 명확하게 전달하고, LLM이 해당 정보를 기반으로 최적의 응답을 생성할 수 있도록 지원합니다. 이를 통해 LLM의 성능을 향상시키고, 사용자와의 상호작용을 보다 효율적으로 만들 수 있습니다.
LLM vs LLM Agent
정의
LLM
LLM Agent
주요 역할
대형 언어 모델 (예: GPT-3, GPT-4)
LLM을 활용한 에이전트 (예: AutoGPT, BabyAGI)
Tool 사용
자연어 처리, 텍스트 생성 등 단일 작업 수행
복잡한 태스크를 계획하고 실행하는 에이전트
Memory
없음 (컨텍스트 제한)
있음 (대화 기록, 작업 히스토리 기억 가능)
Tool 사용
불가능 (내부 모델 능력에만 의존)
가능 (API, 계산기, DB, 웹 브라우저 등 외부 리소스 활용)
Planner
없음
있음 (도구를 언제/어떻게 쓸지 판단하고 순서 계획)
실행 방식
정적인 응답 → 입력 1개 → 출력 1개
동적인 행동 → 입력 → 툴 호출 → 결과 해석 → 반복
유연성
낮음 (미리 학습된 지식만 활용 가능)
높음 (실시간 검색, 계산, API 활용 가능)
지속적 작업
어려움
가능 (여러 단계를 거치는 장기 과제 처리)
즉 LLM은 단순히 입력에 대한 응답을 생성하는 반면, LLM Agent는 LLM을 활용하여 복잡한 작업을 계획하고 실행하는 에이전트입니다.
LLM Agent는 도구를 사용하고, 기억을 유지하며, 동적인 행동을 통해 지속적인 작업을 수행할 수 있는 능력을 갖추고 있습니다. 이러한 차이로 인해 LLM Agent는 더 유연하고 강력한 기능을 제공합니다.
LLM Agent 개발의 파편화와 표준화 필요성
LLM Agent 구축하는 방식은 프레임워크마다 제각각이었습니다.
툴 연동방식이 서로 다름
재사용과 확장성 부족
시스템간 연동이 어려움
따라서 표준 프로토콜의 필요성이 커졌고 해결책으로 MCP 등장
MCP의 기본구조
Host, Client, Server
Host
LLM Application 자체로 통신의 중심이며 여러 개의 Client 포함하고 이들을 관리
특징
LLM 기반의 인터페이스
내부에 MCP Client 포함
user interface 상호작용
Client와 연결된 Server들의 실행 결과와 context를 통합해 LLM에 전달
역할 요약
MCP Client들의 초기화 및 라이프사이클 관리
인증 및 권한 제어
여러 Client로부터 받은 정보를 Context로 통합
사용자와 LLM 사이의 브릿지
ex) Claude 사이트, Cursor AI Code Editor
Client
MCP 서버들과 연결되어있으며, 양방향 메시지 교환 기능 목록 관리, 초기 협상등을 수행
특징
MCP 서버와 연결 (1:1)
내부적 메시지 교환 기능, 서버 상태, 기능 목록
각 Client는 특정 목적에 맞춰 설계
역할 요약
stateful connection
메시지 라우팅 처리
Server
LLM이 외부 세계와 상호작용 할 수 있도록 도와주는 역할
특징
Tool
외부 API 또는 기능 실행 명령 단위, LLM이 호출 가능
Resource
텍스트, 로그, DB 스키마 등 외부 Conext 제공
Prompt Template
LLM이 따라야 할 지시문, 형식
역할 요약
LLM이 직접 호출할 수 있는 도구(Tool) 제공
Host/Client가 요청하는 리소스 정보 제공
LLM의 행동을 안내할 프롬프트 템플릿 제공
초기화 과정에서 프로토콜 협상 수행
Client로부터 받은 요청을 처리하고 응답 반환
기반 LLM Agent의 동작 흐름
LLM : 일반적인 대화, 단순 질의
LLM Agent : 복잡한 작업 수행, 외부 정보 필요
LLM
사용자 입력: 사용자가 LLM에 질문 또는 요청을 입력합니다.
LLM 처리: LLM은 메시지 해석하여 Tool 호출 여부 결정하고, 필요한 경우 내부 지식 기반에서 응답을 생성합니다.
응답 반환: MCP Client를 통해 사용자에게 응답을 반환합니다.
LLM Agent
사용자 입력: 사용자가 LLM Agent에 질문 또는 요청을 입력합니다.
LLM Agent 처리: LLM Agent는 입력을 분석하고, 필요한 경우 Tool 호출 여부를 결정합니다.
Tool 호출: LLM Agent는 필요한 Tool을 찾은 후, 적절한 파라미터 추출합니다.
Tool 실행: Client는 요청값을 JSON-RPC 형식으로 변환
Tool 응답 처리: MCP Server로 요청을 보낸 후, MCP에서는 Tool 관련 기능을 호출하여 처리합니다.
응답 반환: Tool의 실행 결과(JSON-RPC)를 LLM Agent로 반환하고, LLM Agent는 이를 해석하여 최종 사용자에게 응답을 전달합니다.
-
리액트 시작하기 (1 ~ 5장)
리액트 설치 방법
npm 설치
node 설치
툴은 원하는대로
JSX 문법
자바스크립트 확장 문법
JavaScript + XML/HTML
ex) 예시
const element = <h1>Hello, world!</h1>;
원리
React.createElement(
type,
[props], ## 속성
[...children] ## 자식 엘리먼트
)
JSX 문법의 장점
가독성 향상: HTML과 유사한 문법으로 작성되어, 코드가 더 직관적이고 이해하기 쉬움
Injection 방지: JSX는 자바스크립트 표현식을 안전하게 HTML에 삽입할 수 있도록 하여, XSS 공격을 방지하는 데 도움을 줌
{} 가 들어가면 무조건 자바스크립트 코드로 바뀜
Rendering Element
Elements의 정의와 생김새
어떤 물체를 구성하는 성분
리액트 앱을 구성하는 가장 작은 블록들
화면에 보이는 것들을 기술
리액트(Elements)는 자바스크립트 객체 형태로 존재
Elements의 특징 및 렌더링하기
불변성 (immutable)
변경된 부분을 계산하여 해당 부분만 다시 렌더링
Root dom node
ReactElement : React 의 Virtual DOM에 존재
DOMElement : 실제 브라우저의 DOM에 존재
ReactDOM.render()를 통해 ReactElement를 DOMElement로 변환
Components 와 Props
컴포넌트의 정의
컴포넌트는 UI를 구성하는 독립적인 재사용 가능한 코드 블록
컴포넌트는 함수나 클래스 형태로 정의되며, 입력값(Props)을 받아 UI를 렌더링
컴포넌트는 상태(State)를 가질 수 있으며, 상태가 변경되면 UI가 자동으로 업데이트됨
컴포넌트는 계층 구조로 구성되어, 상위 컴포넌트가 하위 컴포넌트를 포함할 수 있음
컴포넌트는 Props를 통해 데이터를 전달받아 렌더링
컴포넌트는 재사용 가능하고, 유지보수가 용이함
Props의 정의
Props는 컴포넌트에 전달되는 입력값 (React의 속성)
ReadOnly 속성으로, 컴포넌트 내부에서 변경할 수 없음
모든 리액트 컴포넌트들은 그들의 Props에 관해서는 Pure(순수)함수 같은 역할을 해야한다.
Pure : 입력값이 동일하면 항상 동일한 출력을 반환하는 함수
모든 리액트 컴포넌트는 Props를 통해 데이터를 전달받아 렌더링해야한다.
function App(pros){
return (
<Profile
name="John Doe"
age= {30}
/>
);
}
function App(pros){
return (
<Layout
width={100}
height={200}
header={<Header title="제목"/>}
footer={<Footer/>}
/>
);
}
Props의 특징 사용법
Props는 컴포넌트에 전달되는 데이터로, 컴포넌트의 동작과 렌더링을 제어
function App(props) {
return (
<Profile
name="소플"
introduction="안녕하세요, 소플입니다."
viewCount={1000}
/>
);
}
Component 만들기 및 랜더링
클래스 컴포넌트, 함수 컴포넌트
주로 함수 컴포넌트를 사용한다함
그래도 클래스 컴포넌트를 알고있어야 한다.
함수 컴포넌트
react Component를 일종의 함수로 생각한다.
function Welcome(props) {
return <h1>안녕하세요, {props.name}님!</h1>;
}
클래스 컴포넌트
javascript es6 문법을 사용하여 만든 컴포넌트
class Welcome extends React.Component {
render() {
return <h1>안녕하세요, {this.props.name}님!</h1>;
}
}
컴포넌트의 이름
항상 대문자로 시작해야함 (소문자로 입력하면 DOM 엘리먼트로 인식됨)
const element = <Welcome name="소플" />;
컴포넌트 렌더링
function Welcome(props) {
return <h1>안녕하세요, {props.name}님!</h1>;
}
const element = <Welcome name="소플" />;
ReactDOM.render(
element,
document.getElementById('root')
);
컴포넌트 합성과 추출
컴포넌트 합성
복잡한 화면을 여러개의 Components로 나눠서 구현
function App() {
return (
<div>
<Header />
<MainContent />
<Footer />
</div>
);
}
컴포넌트 추출
큰 컴포넌트를 작은 컴포넌트로 나누어 재사용성을 높임
function Component(props) {
return (
<div className="content">
<div className="user-info">
<img className="avatar"
src={props.author.avatarUrl}
alt={props.author.name}
/>
<div className="user-info-name">
{props.author.name}
</div>
</div>
<div className="comment-text">
{props.text}
</div>
<div className="comment-date">
{formatDate(props.date)}
</div>
</div>
);
}
Avatar 추출
function Avatar(props) {
return (
<img className="avatar"
src={props.user.avatarUrl}
alt={props.user.name}
/>
);
}
function Component(props) {
return (
<div className="content">
<div className="user-info">
<Avatar user={props.author} />
<div className="user-info-name">
{props.author.name}
</div>
</div>
<div className="comment-text">
{props.text}
</div>
<div className="comment-date">
{formatDate(props.date)}
</div>
</div>
);
}
UserInfo 추출
function UserInfo(props) {
return (
<div className="user-info">
<Avatar user={props.user} />
<div className="user-info-name">
{props.user.name}
</div>
</div>
);
}
function Component(props) {
return (
<div className="content">
<UserInfo user={props.author} />
<div className="comment-text">
{props.text}
</div>
<div className="comment-date">
{formatDate(props.date)}
</div>
</div>
);
}
(실습) 댓글 컴포넌트 만들기
Comment.jsx
```jsx
import React from “react”;
function Comment(props) {
return (
<div>
<div>
</div>
<div>
{props.name}
{props.comment}
</div>
</div>
); }
export default Comment;
- CommentList.jsx
```jsx
import React from "react";
import Comment from "./Comment";
const comments = [
{ name: "test", comment: "안녕하세요 test." },
{ name: "test2", comment: "안녕하세요 test2." },
{ name: "test3", comment: "안녕하세요 test3." },
];
function CommentList(props) {
return (
<div>
{comments.map((comment) => {
return (
<Comment name={comment.name} comment={comment.comment}/>
)
})}
</div>
);
}
export default CommentList;
App.jsx
const root = createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<CommentList />
</React.StrictMode>,
document.getElementById('root')
);
-
EC2 란
EC2 란
클라우드 공간에 크기가 유연하게 변경되는 가상 서버 기능 제공
가상서버이기에 디스크 용량, CPU, 메모리 등을 자유롭게 조정 가능
비용 지불 방법
온디맨드: 사용한 만큼만 지불, 예측 불가능한 트래픽에 적합
리저브드: 저렴한 비용, 1~3년 임대, 인스턴스 크기 변경 불가능
스팟 인스턴스: 입찰, 단기 작업에 적합
EBS 란
EBS란
파일 및 오브젝트 보관할 수 있는 스토리지 볼륨
인스턴스 종료되어도 데이터 유지
가용 영역을 설정해줘야함
가용영역
서울 안에 A, B 가용영역이 존재, 이유는 재해복구 (셧다운)
ELB
ELB란
(Elastic Load Balancer) 로드 밸런싱 서비스로, 여러 인스턴스에 트래픽을 분산시켜 가용성과 안정성 향상
ELB 타입
ALB(애플리케이션 로드 밸런서)
Application OSI Layer 에서 작동. Http/HTTPS 트래픽을 처리하며, URL 기반 라우팅, 호스트 기반 라우팅, WebSocket 지원 등 다양한 기능 제공
ALB 의 고급 설정을 통해 원하는 서버로 직접 라우팅할 수 있다.
개발자가 라우팅 커스터마이징 가능
NLB(네트워크 로드 밸런서)
Network OSI Layer 에서 작동. TCP/UDP 트래픽을 처리하며, 높은 성능과 낮은 지연 시간 제공
CLB(클래식 로드 밸런서)
가장 오래된 로드 밸런서로, HTTP/HTTPS 및 TCP 트래픽을 처리. ALB와 NLB에 비해 기능이 제한적이며, 현재는 사용이 권장되지 않음
ELB 에서 흔히 일어날 수 있는 에러
504 에러
ELB가 백엔드 인스턴스에 요청을 전달했지만, 응답을 받지 못한 경우 발생
백엔드 인스턴스가 과부하 상태이거나, 네트워크 문제로 인해 응답이 지연되는 경우가 많음
해결 방법:
백엔드 인스턴스의 상태 확인
인스턴스의 CPU, 메모리 사용량 모니터링
네트워크 설정 및 보안 그룹 확인
X-Forwarded-For 헤더
Http 요청을 로드밸런서가 받을 때, 출처 에 대한 정보를 담고있음
헤더 정보의 흐름
퍼블릭 IP 주소 -> private IP 주소로 이동
-> private IP 주소엔 출처에 대한 정보가 없다.
-> X-Forwarded-For 헤더에 출처 정보가 담겨있다
-> 기존 public IP address를 찾을 수 있다.
-
Markdown 문법
Markdown 문법
게시물 작성하면서, Markdown을 많이 사용할텐데, 요약집처럼 정리해보았다.
제목
# 제목 1
## 제목 2
### 제목 3
#### 제목 4
##### 제목 5
###### 제목 6
제목 1
제목 2
제목 3
제목 4
제목 5
제목 6
<h1>, <h2>는 다음과 같이 표현할 수도 있습니다.
제목 1
======
제목 2
------
제목 1
제목 2
강조
이텔릭체 : *별 기호* 혹은 _언더바_
볼드 : **별 기호** 혹은 __언더바__
__*이텔릭체*와 볼드__ 혼용 가능
취소선 : ~~물결 기호~~
밑줄 : 직접 `<u></u>` <u>밑줄</u> 사용
이텔릭체 : 별 기호 혹은 언더바
볼드 : 별 기호 혹은 언더바
이텔릭체와 볼드 혼용 가능
취소선 : 물결 기호
밑줄 : 직접 <u></u> 밑줄 사용
목록
`-`로 시작하는 순서가 없는 목록으로 구분합니다.
1. 순서가 있는 항목
1. 순서가 있는 항목
- 순서가 없는 항목
- 순서가 없는 항목
1. 순서가 있는 항목
1. 순서가 있는 항목
- 순서가 없는 항목
- 순서가 없는 항목
- 순서가 없는 항목
- 순서가 없는 항목
-로 시작하는 순서가 없는 목록으로 구분합니다.
순서가 있는 항목
순서가 있는 항목
순서가 없는 항목 (4depth)
순서가 없는 항목 (4depth)
순서가 있는 항목
순서가 있는 항목
순서가 없는 항목
순서가 없는 항목
순서가 없는 항목
순서가 없는 항목
링크
[이름](링크)
[이름](링크 "설명")
[이름][참조]
[참조]: 링크
[참조]: 링크 "설명"
링크 문법 구조
[GOOGLE](https://google.com)
[NAVER](https://naver.com "링크 설명(title)을 작성하세요.")
[상대적 참조](../users/login)
[Dribbble][Dribbble Link]
[GitHub][1]
문서 안에서 [참조 링크]를 그대로 사용할 수도 있습니다.
다음과 같이 문서 내 일반 URL이나 꺾쇠 괄호(`< >`, Angle Brackets)안의 URL은 자동으로 링크를 사용합니다.
구글 홈페이지: https://google.com
네이버 홈페이지: <https://naver.com>
[Dribbble Link]: https://dribbble.com
[1]: https://github.com
[참조 링크]: https://naver.com "네이버로 이동합니다!"
GOOGLE
NAVER
상대적 참조
Dribbble
GitHub
문서 안에서 참조 링크를 그대로 사용할 수도 있습니다.
다음과 같이 문서 내 일반 URL이나 꺾쇠 괄호(< >, Angle Brackets)안의 URL은 자동으로 링크를 사용합니다.
구글 홈페이지: https://google.com
네이버 홈페이지: https://naver.com
이미지


![대체텍스트][참조]
[참조]: 이미지주소
[참조]: 이미지주소 "설명"
")
![이미지입니다!][Image]
[Image]: https://picsum.photos/500/300 "이미지입니다!"
이미지에 링크 추가
[](https://dmstjd1024.github.io/)
코드 강조
`background` 혹은 `background-image` 속성으로 요소에 배경 이미지를 삽입할 수 있습니다.
background 혹은 background-image 속성으로 요소에 배경 이미지를 삽입할 수 있습니다.
블록
// 연속 백틱 3개 시작, 종료 구조
```언어이름
내용
```
function add(a, b = 1) {
console.log(a, b)
return a + b
}
$ npm run dev
s = "Python syntax highlighting"
print s
No language indicated, so no syntax highlighting.
But let's throw in a <b>tag</b>.
백틱 기호 사용
\`
<code>\`</code>
`
\`
표
| 헤더 | 헤더 | 헤더 |
|---|---|---|
| 셀 | 셀 | 셀 |
| 셀 | 셀 | 셀 |
헤더 | 헤더 | 헤더
---|---|---
셀 | 셀 | 셀
셀 | 셀 | 셀
값
의미
기본값
static
유형(기준) 없음 / 배치 불가능
static
relative
요소 자신을 기준으로 배치
absolute
위치 상 부모(조상)요소를 기준으로 배치
fixed
브라우저 창을 기준으로 배치
sticky
스크롤 영역 기준으로 배치
값
의미
버티컬바 출력
|
인라인 코드 강조
\|
인용문
> 인용문 - 남의 말이나 글에서 직접 또는 간접으로 따온 문장.
> _(네이버 국어 사전)_
BREAK!
> 인용문을 작성하세요!
>> 중첩된 인용문(nested blockquote)을 만들 수 있습니다.
>>> 중중첩 인용문 1
>>> 중중첩 인용문 2
>>> 중중첩 인용문 3
인용문 - 남의 말이나 글에서 직접 또는 간접으로 따온 문장.
(네이버 국어 사전)
BREAK!
인용문을 작성하세요!
중첩된 인용문(nested blockquote)을 만들 수 있습니다.
중중첩 인용문 1
중중첩 인용문 2
중중첩 인용문 3
원시 HTML
<img width="150" src="http://gstatic.com/webp/gallery/4.jpg" alt="Prunus" title="마크다운은 이미지의 크기를 지정할 수 없으므로, 크기 지정을 위해서는 <img> 태그를 사용해야 합니다.">

수평선
---
***
___
줄바꿈
동해물과 백두산이 마르고 닳도록
하느님이 보우하사 우리나라 만세
무궁화 삼천리 화려 강산 <!--띄어쓰기 2번--> 대한
사람 대한으로 길이 보전하세<br>
끝!
동해물과 백두산이 마르고 닳도록
하느님이 보우하사 우리나라 만세
무궁화 삼천리 화려 강산 대한
사람 대한으로 길이 보전하세
끝!
주석
-- 시작 --
[//]: # (주석은 이렇게 작성합니다.)
[//]: # (이 주석은 렌더링되지 않습니다.)
[//]: # (주석은 이렇게 작성합니다.)
[//]: # (이 주석은 렌더링되지 않습니다.)
-- 끝 --
– 시작 –
– 끝 –
[참조]
markdown guide
HEROPY.DEV
-
-
-
-
Github Action VS Code Pipeline
Github action 이란
Github에서 공식적으로 제공하는 CI / CD 툴
코드 변경 문제사항 검사
소프트웨어 빌드, 상용서버 배포
통계 데이터 수집
코어 개념
Workflow
자동화된 전체 프로세스. 하나 이상의 Job으로 구성되고, Event에 의해 예약되거나 트리거될 수 있는 자동화된 절차를 말한다. ex) deploy.yml
Workflow 파일은 YAML으로 작성되고, Github Repository의 .github/workflows 폴더 아래에 저장된다. Github에게 YAML 파일로 정의한 자동화 동작을 전달하면, Github Actions는 해당 파일을 기반으로 그대로 실행시킨다.
Event
Workflow를 트리거(실행)하는 특정 활동이나 규칙. ex) pull request, push 시 event 실행
Job
Job은 여러 Step으로 구성되고, 단일 가상 환경에서 실행된다. 다른 Job에 의존 관계를 가질 수도 있고, 독립적으로 병렬로 실행
Step
Job 안에서 순차적으로 실행되는 프로세스 단위. step에서 명령을 내리거나, action을 실행할 수 있다.
Action
job을 구성하기 위한 step들의 조합으로 구성된 독립적인 명령. workflow의 가장 작은 빌드 단위이다. workflow에서 action을 사용하기 위해서는 action이 step을 포함해야 한다. action을 구성하기 위해서 레포지토리와 상호작용하는 커스텀 코드를 만들 수도 있다. 사용자가 직접 커스터마이징하거나, 마켓플레이스에 있는 action을 가져다 사용할 수도 있다.
Runner
Gitbub Action Runner 어플리케이션이 설치된 머신으로, Workflow가 실행될 인스턴스
ex)
name: dmstjd1024-github-actions # WorkFlow 이름
on: # 트리거 하는 이벤트 명시
push: # push 이벤트일때
branches: [ master, dev ] # 어떤 브랜치들
pull_request: # 어떤 풀리퀘일때
branches: [ master ]
paths: # 특정 패턴을 설정하여 변경되었을 때 이벤트 실행
- "**.js"
schedule:
- cron: '0 01 * * *' # 새벽 1시 (https://crontab.guru 참조)
jobs:
build:
strategy:
matrix: # 테스트 배포를 위한 빌드 matrix 구성
node-version: [10.x, 12.x]
runs-on: ubuntu-latest # 어떤 OS로 실행
steps: # Job이 가질 수 있는 동작 나열 step 독립적 프로세스
- name: Checkout source code # Step 이름
uses: actions/checkout@v2 # Step에서 사용할 액션 (Github 마켓플레이스 action 사용가능)
- name: My First Step # step 이름
run: # job에 할당된 자원 shell 이용하여 커맨드 라인 실행
npm install
npm test
npm build
- name: Cache yarn dependencies
uses: actions/cache@v1
id: yarn-cache
with: # action에 정의되는 input 파라미터 (환경 변수)
path: node_modules
key: $-yarn-$
restore-keys: |
$-yarn-
Code Pipeline
가격 비교
Github Action(Teams 플랜)
Code Pipeline
갯수별 비용
X
활성 파이프라인(사용기간 30일 이상, 코드 1회 변경) 개당 1.00 USD(생성 후 첫 30일간 무료) ※ 월별 무료 활성 파이프라인 1개 지원
패키지 스토리지
2GB
X
분(월별)
3,000분
X
분당 요금
vCPU 2개 기준 $0.008 (약 10.80원)
X
월별 총 비용
$4 (연 결제 기준 $3.67)
Resource 에 따라 다양한 비용 발생 (측정 불가) ※ 월별 명세서로 측정 예정
추가) CodeDeploy 비용
AWS 답변 : AWS CodeDeploy를 통해 Amazon EC2 인스턴스에 코드를 배포하는 데는 추가 비용이 부과되지 않습니다.
-
Clean Code 후기
1장 [깨끗한 코드]
회사가 망한 원인은 바로 나쁜 코드 탓..
나쁜 코드는 개발 속도를 크게 떨어트린다. → 현재 기반을 잡는 상태이기에 여러 상황에 따라 유연히 대체 가능한 코드를 구현해야한다.
나쁜 코드의 위험을 이해하지 못하는 관리자 말을 그대로 따르는 행동은 전문가답지 못하다 → 관리자 측의 압박에 무너지지 말고 나의 기준이 있어야 된다.
그러면 좋은 코드란?
check list: 우아한, 효율적, 논리가 간단, 의존성 줄이기, 성능 최적, 깔끔한 오류 처리, 한가지마 집중, 가독성, 다른사람이 고치기 쉽게, 테스트 케이스, 중복이 없음, 짐작대로 기능이 수행되게
우리가 항상 모든 코드에 있어 체크리스트를 놓치는 부분이 있을 수 있다. 항상 코드를 깨끗하게 유지하는 것(정리)을 놓치지 말자
2장 [의미있는 이름]
클린 코드는 복수형인 경우 s를 붙이거나, 변수명에 복수를 의미하는 명을 추가 ex) Accounts, accountGroup
List에 있어 단어는 특수하다. → 우리들은 어떻게 정의를 내리면 좋을까?
흡사한 이름에 대한 주의! – ex) 현재 User 기능 중, User 대해 정보를 가져올 때, 두 방식으로 가져올 수 있는데(security, db) 이에 이름을 짓는 것이 고민
하나의 데이터를 계속 가공하다보면 불용어(AccountData, AccountInfo) 에 대한 유혹이 많아지는데, 이 함정에 주의하자
각 자바 표현에 대한 명명
클래스 이름 = 명사, 명사구
메서드 이름 = 동사, 동사구
생성자 중복정의 시 정적 팩토리 메서드 사용 ( 메서드 함수명에 의미가 담겨있다)
이름을 지을 때 개발적 관점에서 짓는 것에 동의한다.
좋은 이름에 대한 선택은 모두가 생각하는 것에 따라 순위가 다를 수 있다.
또 이름을 바꾸는 것에 반대가 두렵기도 하고, 내가 신중히 고민하여 작명한 이름이 거절당할때의 허탈함이 있을 수 있다.
이 책에서 저자는 오히려 좋은 이름으로 바꾼다 하면 ‘반갑고 고맙다 하는 마인드’를 따라 가는 것이 더 나아질 수 있는 덕목이라 생각하여 스스로 유연해질 수 있도록 노력해야겠다.
3장 [함수]
함수 규칙은 ‘작게->작게’ 근데 제일 좋은 작게 어느정도? (클린코드는 들여쓰기 2단 넘으면 x)
한가지 일만하는 것, 그 일을 잘하는것, thumbs up (Util 성처럼 사용해야하나?)
Switch 문에 대해 추상 팩토리에 숨겨야 한다?
함수로 상태를 변경해야 할 때는 함수가 속한 객체 상태 변경을 택해라
try catch 같은 경우, 하나의 함수로 만들어서 따로 빼는 것으로 만들어 exception 처리 관리를 쉽게하자
중복제거를 위해 코드를 최대한 부모 클래스에 몰아 넣는다.
함수짜는 법 → 줄줄이 나열 → 다듬기 → 이름바꾸기 → 쪼개기 → 좋은 함수
4장 [주석]
함수를 만들 때마다 주석을 달았던 기억이 났다.
책에서의 저자는 주석에 대해 좋게 바라보지 않는다. but 나는 주석 자체를 나쁘게 보지 않는다. 함수가 바뀌면서 주석이 사라지는 것은, 그만큼 좋은 코드로 바뀌었다는 것이고, 허나 저자와 나의 공통점은 주석을 어떻게 적냐는 것이다.
최대한 함수의 서술적 방식을 이용해 주석에 대한 기능을 대체할 수 있도록 해라.
내가 생각하는 주석을 넣어야 하는 부분이라면, 코드 내에 오류가 날 수 있거나, TODO 부분, 패턴에 대한 설명 등이다. 그리고 클래스 파일을 열었을 때, 코드 중에서 꼭 한번 확인해보고 소스를 읽어야 하는 것을 기준으로 작성하는 것이 좋다 생각한다.
5장 [형식맞추기]
이번 장 같은 경우는 저희는 IDE 툴이나 여러 auto complete 기능들을 통해 잘 지켜진다 여겨지고 있으며,
개인적인 느낌으로는 저자측의 사례는 옛날 코딩에 대한 사례를 예시로 들어 좀 더 강조하려는 느낌이 들긴 한다.
종속 함수나 개념적 유사성에 대한 내용들만 잘 지킬 수 있다면, 형식 맞추기에 있어서 큰 문제가 되지 않는다 생각한다.
6장 [객체와 자료 구조]
추상 인터페이스를 제공 해사용자가 구현을 모른 채 자료의 핵심을 조작할 수 있어야 진정한 의미의 클래스이다.
디미터 법칙 : 모듈은 자신이 조작하는 객체의 속사정을 몰라야 한다.
기차충돌은 조잡하다 → 디미터의 법칙에 어긋난다. → 그럼 한번만 부르는 상황이여도 매번 나눠야 하나?
그럼 기차충돌을 묶어놓은 하나의 함수를 만드는것?
DTO: 가공되지 않은 정보를 어플리케이션 코드에 사용할 객체로 변환하는 구조체.
활성 레코드에 비즈니스 규칙 메서드 추가는 바람직하지 않음
7장 [오류 처리]
오류코드보다 예외 사용
void send(){
try{
sendMail();
}catch(MailException e){
log.error(e);
}
}
void sendMail() throws MailException{
...
connectStibee();
...
disconnect();
}
void connectStibee(){
...
}
void connectStibee(){
...
if(connectFail){
throw new MailException("메일 전송 실패");
}
}
캡슐화하여 나타내면 연결 해제하는 로직과 오류를 처리하는 로직을 분리(독립적)
Try Catch Finally
try 안에서 무슨일이 생기든 catch 는 일관성있게 유지
그래서 작성하다 try catch문이 나오는 상황이 오면, 일단은 try catch finally문을 먼저 작성해놓고 코드를 작성해보자
작성 후 리팩토링 과정을 거치며 논리를 추가하는 방식을 구현해보자 (트랜잭션의 본질 유지를 위해)
미확인 예외를 사용하라
확인된 오류가 치르는 비용에 상응하는 이익을 제공하느냐 : 캡슐화를 하면서 catch 블록을 여러개 올릴 때가 있는데, 이때 하위 코드를 변경 시 상위 코드에 catch를 추가 또는 throw 절을 모두 추가하는 즉, 전부 고쳐야 할 때가 있다. 이런 일은 대체로 외부의 라이브러리를 사용할 때 나타난다.
예외에 의미를 제공하라
오류메시지에 정보를 담아서 예외를 함께 던져라
호출자를 고려해 예외 클래스를 정의하라
우리는 오류가 발생할 때, 오류를 잡아내는 방법을 중요시 생각해야한다 말한다. 전 회사에서 작업했을 때, 공공기관에서 취약점에 의해 모든 오류에 대한 exception을 catch 해야해서 각 try catch마다 catch문을 계속 붙였던 기억이 난다. 그때는 막무가내로 붙였는데, 책은 wrapper를 통해 감싸서 던지라고 제안한다. 이부분에 대해 좋다 생각한다. 책에서 LocalPort 같은 내용을 예시로 들었는데, PortDeviceFailure로 묶어서 하나로 바꾼 방식은 open 함수에 대한 모든 Exeption을 하나로 처리할 수 있어서 좋다 생각한다.
정상 흐름을 정의하라
위에처럼 캡슐화를 해서 작업하면 비즈니스와 오류처리가 잘 분리된다 이야기한다. → 허나 분리될 때가 적합하지 않을때가 있다.
예시처럼 m_total += expenses.getTotal() 함수를 catch 시에도 작성하는 것보다 PerDiemMealExpenses implements MealExpenses 로 묶어서 거기다 처리하는 방식 -> 특수사례를 처리하는 패턴
null을 반환하지 마라
null을 반환할거면 Exception을 던지거나 List 같은 경우, Collections.emptyList()를 던져라
null을 전달하지 마라
메서드로 null을 전달하는 코드를 피해라, 이것같은 경우는 매번 들어오는 함수에 대한 처리도 비용낭비이며 저나느 null을 넘기지 않도록 금지하는 정책을 추천했다.
As a result
우리는 앞단의 내용들을 사용하면서 오류처리에 대한 건도 같이 생각해야한다. 깨끗한 코드만 바라보다 정작 오류만 빵빵터지는 프로그램이 될 수도 있기 때문이다.
8장 [경계]
오픈소스, 패키지, 컴포넌트 .. 우리는 모든 것을 직접 손으로 구현할 수 없기에 다른 코드들을 가져올 때가 많다. 이때 깔끔하게 통합하는 법을 알아야한다.
외부코드 사용하기
Map같은 경우 유연성이 좋고 Map 내의 데이터 관리 및 제공 기능이 많다. 허나 그만큼 외부에서도 사용할 수 있는 기능(함수 등등..) 많고, 상태변화에 대해 취약하다. ex) 객체유형
Map을 깔끔하게 사용하려면 객체안에 숨긴다. 이러면 외부에서 접근 할 때 사용할 수 있는 내용이 객체가 선언한 기능으로 제한된다. 허나 중요한 것은 Map을 여기저기에 넘기지 말라한다.(노출주의)
경계 살피고 익히기
타 라이브러리를 우리쪽 코드로 작성하면, 우리버그인지 라이브러리 버그인지 골치아프다.
학습 테스트 : 외부 코드를 우리 코드로 호출하게 하며, 테스트 케이스를 작성해 외부 코드를 익혀라
아직 존재하지 않는 코드를 사용하기
아는 코드와 모르는 코드를 분리하는 경계 : 다른 모듈에 대한 코드를 모를 때 자체적 인터페이스를 정의하여 구현하기
깨끗한 경계
경계를 나누다보면 변경이 될 일들이 생긴다. 소프트웨어 설계가 매우 훌륭하다면 이런 일에 비용이 크게 발생하지 않는다. 따라서 경계에 위치하는 코드는 깔끔하게 분리하면 좋다. Adapter 패턴 이나 외부 라이브러리를 감싸서 우리의 기능으로 사용하자.
9장 [단위 테스트]
나는 팀원 분들과 작은 이야기 속에서, 테스트 코드 작성을 좋아하지 않는다고 말했다.
왜 그렇게 대답했나면, 지금 현재 작성중인 코드들은 단순 CRUD이며, 현재 프로젝트 코드를 Converting하는 작업에 있고,
입사 후 나만 아직 뚜렷한 결과물을 가져오지 못했기에 스스로 불안함이 있기 때문이다.
테스트 코드를 중요하게 생각하는 것에 있어서는 동의한다. 허나 아직 핵심 비즈니스 로직 외에는 테스트 코드 작성을 여유있게 둘 정도로 생각하지 않기 때문이다.
그렇다고 팀원들과의 이야기에서 고지식하게 전 테스트 코드를 왜 작성합니까! 라고 말하는 것은 아니고,
불안감에 망설이는 중이다. (2023.11.19 기준)
TDD 법칙 3가지
실패하는 단위 테스트를 작성할 때까지 실제 코드를 작성하지 않는다.
컴파일은 실패하지 않으면서 실행이 실패하는 정도로만 단위테스트 작성한다.
현재 실패하는 테스트를 통화할 정도로만 실제 코드를 작성한다.
이런식으로 작성하면 엄청나게 크고 많은 테스트들이 나오고, 우리는 관리하기가 어려워진다.
깨끗한 테스트 코드 유지하기
저자는 테스트 코드는 실제 코드 못지 않게 중요하다 강조한다!
깨끗하게 짜는 법
가독성 2. 가독성 3. 가독성 → 명료성, 단순성, 풍부한 표현력
BUILD-OPERATE-CHECK 패턴 : 테스트 자료 만들기 → 조작 → 결과 체크 (given-when-then)
도메인에 특화된 테스트 언어( 간결하고 표현력이 풍부한 코드로의 리팩토링)
이중 표준 : 실제 코드만큼 효율적인 필요는 없다.
저자가 추천하는 것은 각 상태에 대해 함수로 따로 빼서 작성을 진행하던지, StringBuffer를 통해 원하는 상태랑 assertEquals를 하라는 것이다.
테스트당 Assert 하나
테스트에 assert가 여러개 필요하다면, 테스트를 쪼개서 각 assert를 수행하라
given-when-then 관례를 사용하여 쪼개놓고 Template Method 패턴으로 Then 부분을 자식 클래스에 두면 된다.
허나 Assert 하나하나 나누면 나중에 많은 변수들을 체크할 때 복잡해질 수도 있다.
테스트당 개념 하나
F.I.R.S.T
F = Fast : 빠르게
I = Independent : 독립적
각 테스트는 서로 의존하면 안된다. 하나가 실패하면 나머지도 실패하므로 후반에 힘들어진다.
R = Repeatable: 반복가능하게
모든 환경에서 여러번 테스트해도 돌아가도록 → 안되면 환경 탓(남탓)을 한다.
S = Self-Validating: 자가검증
Boolean으로 결과를 내자.
T = Timely : 적시에
테스트하려는 실제 코드를 구현하기 전에 구현한다.
10장 [클래스]
클래스 체계
변수와 유틸리티 함수는 공개하지 않는 편이 낫지만 반드시 숨겨야 할 필요가 없다.
클래스는 작아야 한다.
클래스 이름은 클래스 책임을 기술해야한다.
단일 책임 원칙(SRP)
변경할 이유가 하나, 단 하나여야 한다.
우리는 잘 지키려고 시작하지만 막상 진행하다보면 여러 책임을 진 코드를 발견할 때가 있다.
프로그램이 돌아가면 일이 끝났다는 생각 → X,
단일 책임 클래스가 많아지면 큰 그림 이해하기 어려워진다 (이 문장은 공감한다)
응집도
인스턴스 변수가 작아야 한다. → 응집도가 높아질 수록 쪼개야 한다.
이러한 기준을 두고 리팩토링 시, 더 좋은 코드로 바뀔 수 있다.
변경하기 쉬운 클래스
유지보수를 하다보면 클래스를 고칠 때가 많았고, 그러다보면 클래스는 변경하기 쉬워야한다는 말에 쫑긋했다.
저자는 추상클래스로 만들어서 처리할 때를 좋다 말한다. (아직까지 추상클래스로는 나눠본 사례는 없기에.. 다른분들의 경험을 들어보고 싶다.)
변경으로부터 격리
예시 코드를 보면 결합도를 낮추는 것이 테스트코드 작성할 때 어떤지 확인 할 일이 생기길..
11장 [시스템]
시스템 제작과 시스템 사용을 분리하라
소프트웨어 시스템은 준비 과정과 런타임 로직을 분리해야한다.
초기화 지연, 계산 지연이란 기법으로 설명했는데, 이러한 설정 논리는 일반 실행 논리와 분리해야 모듈성이 높아진다 설명함.
체계적이고 탄탄한 시스템 : 손쉬운 기법으로 모듈 성을 깨지 않는, 설정 논리와 일반 실행 논리 분리 등등 있다.
Main 분리
시스템 생성과 사용을 분리 → 생성은 main에서 실행은 application에서 하도록 ( @SpringBootApplication 과 비슷한?
팩토리
객체 생성시점을 어플리케이션에서 결정할 필요가 생긴다. 추상 팩토리 패턴을 사용
Factory.create 같이 선언 시점을 어플리케이션에서 결정한다.
의존성 주입
제어 역전 : 한 객체가 맡은 보조 책임을 새로운 객체에게 전적으로 넘긴다.
의존성 관리 : main, 컨테이너 사용(Spring Ioc Container, DI Container)
대다수 컨테이너는 필요할 때까지 객체를 생성하지 않고, 지연이나 최적화에 쓸 수 있도록 호출하거나 프록시 생성하는 방법 제공
확장
처음부터 올바르게 시스템을 만들 수 있다는 믿음은 미신이다.
시스템 아키텍처는 손대는게 큰 비용이다.
관심사를 적절하게 분리해 관리한다면, 아키텍쳐는 발전할 수 있다. ex) Entity → Dto 변환
횡단 관심사
영속성 같은 관심사 = 영속성 프레임워크 모듈화 (spring-context.xml)
횡단 관심사를 대처하기 위해 AOP를 사용한다. Aspect 방식으로 접근
자바 프록시
메서드 호출을 감싸는 경우 인데 코드가 늘어나며 복잡해져 사용 X
순수 자바 AOP 프레임워크
스프링은 비즈니스 논리를 POJO로 구현 (도메인 초점)
예시에서는 app.xml을 통해 접근하는걸로 보여지는데, xmlBeanFactory.getBean("bean 이름");
이렇게하면 java랑은 연관이 있지만, spring이랑은 연관이 없는 독립적 코드이다.
사실 이러한 내용들에 있어서 그렇다는 건 알겠는데, 상세하게 기술이 써진다 이런거는 체감이 부족한 것 같다.
AspectJ 관점
언어 차원에서 관점을 모듈화 구성으로 지원 (어디 언어에서나 사용이 유용함)
애너테이션을 사용하여 쉽게 사용하도록 기능 제공
테스트 주도 시스템 아키텍처 구축
도메인 논리로 아키텍처 구축이 가능하다면, TDD 아키텍처가 가능해진다.
단순하면서 잘 분리된 아키텍처를 진행하여 결과물 출시 후 확장하는 방식도 괜찮다.
의사 결정을 최적화하라
제일 위에가 결정하지말고 가장 적합한 사람한테 책임을 맡겨라.
마지막까지 미루는 것이 최선의 선택일 때도 있다.
명백한 가치가 있을 때 표준을 현명하게 사용하라
우리가 흔히 알고 쓰는 기술과 규칙이 단순 그냥 아니깐 사용하는 건지, 필요함에 있어 사용하는 건지 생각해라
결론 : 추상화 단계에서 의도를 명확히 표현, POJO를 작성해 구현 관심사 분리, 단순한 수단 사용
결과적으로 전부 무슨 내용이고, 어떤 것이 좋다 말할 때, 완벽하게 파악이 잘 되지는 않는다. 허나 이러한 시스템 아키텍처의 중요성이 코드를 작성하고, 유지 확장해나가는데 중요한 것이라는 것은 느꼈고 더 이런 부분까지 생각해서 구현하는 사람이 되어야 한다.
12장 [창발성]
설계는 ‘단순하다’
모든 테스트를 성공적으로 실행
중복 코드 제거
프로그래머 의도를 명확히 표현,
클래스, 메서드 수를 최소로 줄이기
모든 테스트를 실행하라
시스템이 의도한대로 돌아가는 지 검증할 방법이 없다면, 문서 작성에 대한 노력과 비용 가치가 줄어든다.
테스트 케이스를 만들고 계속 돌리면 객체지향적으로 바꾼다.(품질 상승)
리팩터링
코드는 점진적으로 리팩터링
응집도 (상승) 결합도 (하락) 관심사 분리, 모듈화, 함수 클래스 크기 줄이고, 더 나은 이름 선택,
+ 중복 제거, 의도 표현, 클래스 메서드 최소로 줄이기
중복을 없애라
Template Method 패턴을 이용해 중복을 제거
표현하라
좋은 이름 선택
함수 클래스 크기를 가능한 줄인다.
표준 명칭
클래스와 메서드 수를 최소로 줄여라
경험에서 나온 창발성은 매우 중요하다 생각한다. 우리는 이론이 아닌 실무에 있는 사람들이니깐.
허나,(이번에도 허나를 왜치는 나..) 나는 실제로 코드에 대한 경험이 적은 사람들에게는 이 내용을 참고만 하고, 나중에 제대로 접했으면 좋겠다는 생각을 한다.
이론만 가득한 입장에서 신입이나, 회사의 코드를 접했을 때 분명 클린코드의 말에 맞지 않게 해놓은 코드가 대다수라고 생각할거다. (나도 여러 업체들의 소스들을 보았지만, 클린코드처럼 정말 깔끔하게 짜놓은 코드는 없었다.)
그리고 이들의 코드를 보며 나쁜 코드라고 접했고, 그렇게 자신이 리팩토링을 하다보면, 아키텍처까지 가기 전에 결국 고치지 못할 것이라고 생각할 것이다. 그리고 그 코드들은 그 팀원들끼리 규약을 있는 코드이고…
그렇게 팀원들의 코드 스타일, 규약, 템플릿 등을 보며 이사람은 클린 코드 책을 읽지 않았어! 코드를 못짜는 팀이야! 라는 생각을 가지게 될 확률이 높으며 그건 스스로를 좀먹는 일이 될거라 생각하기 때문이다.
그래서 난 이번장은 좋은 내용이자 동시에 위험한 내용(성급한 일반화의 오류)일 수 있다 생각하여 최소 2, 3개의 프로젝트의 시스템들을 겪으며 이 내용을 접목하면 좋겠다.
-
-
-
내가 적어보는 Hateoas 정리
Hateoas
티켓 프로젝트를 진행하면서 rest(ful?) api 문서화를 만드는 중, 우리는 hateoas를 당장 써서 만들어야 할지 아닐지 고민되었다. 그래서 일단 자료조사를 위해서 hateoas에 대해 알아보려고 한다.
Hateoas 란?
Wikipedia에서 ‘hateoas’란 Hypermedia As The Engine of Applicaton state 라는 뜻으로 다른 어플리케이션 아키텍쳐랑 구별되는 REST 어플리케이션 구조 의 요소라고 정의해놨다. hateoas를 사용함으로써 클라이언트는 하이퍼미디어를 통한 다이나믹한 정보를 어플리케이션 서버에 제공하는 네트워크 어플리케이션과 상호작용할 수 있다. 이 제한은 클라이언트와 서버를 분리해주는 것을 도와준다고 말한다.
즉, 쉽게 말해 프론트 부분에서 가공해서 클라이언트한테 지원해줄 필요 없이 hateoas를 사용해 동적으로 상태나 전이를 전달해준다. 이는 더 높은 수준과 rest에 걸맞는 rest(ful?) api를 만들 수 있게 해준다.
Hateoas 사용법
hateos 설정
일단은 의존성을 통해
implementation 'org.springframework.boot:spring-boot-starter-hateoas'
라이브러리를 설정만 해주면 끝난다.
hateoas 사용
예를 들어 내가 보내주고 싶은 ‘AccountDto’가 있다면
AccountCotroller.java
AccountDto accountDto = new AccountDto(); // 예시론 빈 객체 생성인데 보내고 싶은 Dto 부분이라고 생각하면 된다
EntityModel<AccountDto> entityModel = EntityModel.of(accountDto);
entityModel.add(linkTo(AccountController.class).slash(accountDto.getId()).withSelfRel() );
// 자신 컨트롤러와 매핑된 URL, 주어진 accountDto의 Id 값을 가져와 withSelfRel()을 사용해 해당 url이 자기 참조라는 것을 추가해 entityModel에 넣어준다.
entityModel.add(linkTo(AccountController.class).slash(accountDto.getId()).withSelfRel("my") );
// 자신 컨트롤러와 매핑된 URL, 주어진 accountDto의 Id 값을 가져와 withSelfRel()을 사용해 my라는 relation을 만들어준다.
entityModel.add(linkTo(AccountController.class).slash(accountDto.getId()).withSelfRel() );
// 자신 컨트롤러와 매핑된 URL, 주어진 accountDto의 Id 값을 가져와 withRel()을 사용해 self라는 relation을 만들어준다.
entityModel.add(Link.of("/docs/index.html#resources-account-get").withRel("profile"));
// 이 부분은 내가 임의로 지정한 부분에 relation을 만들어 추가해준다 (여기서는 restdocs에 지정한 부분으로 이동하게).
여기서 linkTo()와 Link.of() 두가지가 나오는데, linkTo()는 내가 원하는 Object를 찾아 linkTo()가 제공하는 다양한 기능을 사용해 내가 리턴해줄 url에 추가로 넣어주는 것이고, Link.of()는 new Link로 임의로 url을 만들어 준 뒤 relation을 추가해준다 생각하면 된다.
리턴 방식은
return ResponseEntity.ok(entityModel);
나 다른 ResponseEntity의 함수나 객체를 생성해 던져주면
원래 리턴 값 ResponseEntity.ok(accountDto);
{
"id":1,
"name":"test",
"email":"test@email.com",
"role":"ROLE_USER"
}
Hateoas 적용
{
- "id":1,
- "name":"test",
- "email":"test@email.com",
- "role":"ROLE_USER",
- "_links":{
"self":{
"href":"http://localhost:8080/api/account/1"
},
"profile":{
"href":"/docs/index.html#resources-account-get"
}
}
}
이런 식으로 application/hal+json 으로 된 JSON을 리턴한다.
적용 후기
일단은 매우 기초적으로 hateoas를 적용해서 던져보았는데 좀 더 찾아보면 EntityModel 안에 다양한 기능들이 있고 이걸 사용하면 self, profile 말고 더 많은 url들을 이용해 다양하게 처리할 수 있다. (ex PageResourceAssembler 를 이용해 동적 페이징도 처리 가능하다.)
이렇게 전송되어 보내면 요청에 대한 URL을 던질 때 동적으로 처리할 수 있어 이후에 URL 정보가 변경되더라도 클라는 일일히 가서 수정할 필요가 없다. (허나 예제에선 그 장점이 두각되진 않았다ㅠ)
hateoas를 dto 안에 넣을 수 있을 까
여기서 우리는 ApiResponseDto 안의 data 라는 제네릭 타입에 집어넣은 상태에서 application/hal+json을 유지할 수 있냐이다.
그리고 여러 구글링을 찾아본 결과
hateoas 랑 Dto 관계?
이 스택오버플로우의 이야기 중 “hateoas는 dto을 대체하지 않는다. dto 위에 구축된다. 따라서 Dto 클래스를 확장해야 한다.”
이걸 읽고 만약 hateoas를 적용해야 한다면 ApiResponseDto에 hateoas가 지원하는 Model을 상속받아 구성해야 하는데, 어떻게 뜯어서 진행해야 할지는 팀원들과 이야기 한 다음에 결정해야 할 것 같다.
-
-
SAP UI5 란
SAP UI5
SAP UI5 란
HTML5, CSS, JavaScript로 구성된 클라이언트 UI 기술이다.
SAP UI5로 개발된 앱들은 우리가 사용하는 장치의 브라우저 안에서 실행한다.
사용자들이 앱을 액세스 할 때, 해당 서버로 애플리케이션을 브라우저에 로드하라는 요청을 전송, View는 SAP UI5에 관련된 라이브러리를 액세스한다. 모델은 또한 인스턴스화되고, 비즈니스데이터는 데이터베이스로부터 가져온다.
SAP UI5가 사용되는 환경에 따라, 라이브러리 또는 애플리케이션을 SAP NetWeaver Application Server 또는 SAP Business Technology Platform 등에 저장할 수 있으며, 비즈니스 데이터(예: SAP Gateway)를 통해 OData 모델을 사용하여 액세스할 수 있다.
프레임워크의 아티팩트는 라이브러리라는 최상의 구조단위로 되어있으며, 라이브러리는 확장성 개념을 가진 아티팩트이다. 이들은 공통으로 사용되는 기본 라이브러리와 함께 미리 정의된다. 라이브러리는 타입들의 제어, 그리고 웹 어플리케이션에 의해 사용되도록 묶어놓았다. 동시에 미리 정의된 sap.m이라는 라이브러리와 나란히 쓰고 사용하기 쉽게 만들어 첫번째로 커스텀 ui 라이브러리로 취급된다.
UI element는 사용자 인터페이스가 기본으로 되어 있으며 속성, 이벤트, 메소드, 관계와 같은 엔티티들을 재사용하며 최고로 중요한 관계는 다른 UI 엘리먼트들을 트리 구조로의 방식으로 생성될 수 있게 집계한다.
개발자가 보는 화면의 관점으로부터 제어 요소는 가장 중요한 아티팩트이다. 이것은 사용자 화면에 나타내는 모든 컨트롤을 제어하는 객체이며 이것은 각 트리구조의 루트로 사용될 수 있는 유저인터페이스 요소의 종류이다. 이 방식은, 특히 렌더링을 위한 엔트리 포인트을 제공한다. 이들은 루트로서 사용될 수 없는 트리구조의 비제어 요소들이다. 그러나 단지 몇몇 부분은 종속적으로 사용된다.
데이터 타입들은 메타 모델 안의 첫번째 클래스로 정의된 엔티티들이다. 타입 시스템의 확장성과 라이브러리 교차타입을 재사용하도록 허락한다. sap.ui.core 라이브러리는 다른 라이브러리들에 사용될 수 있는 타입으로 미리 정의한다.
Bootstrapping
브라우저 실행되자마자 스크립트를 로드함으로 SAP UI5 실행이 자동으로 페이지에 실행한다. 단순한 SAP UI5 기본 설치를 사용하는 경우에 UI를 실행하기에 충분하다. 이외에도 세팅을 통해 너의 어플리케이션 테마나 라이브러리를 설정할 수 있다.
<script id="sap-ui-bootstrap" type="text/javascript"
src="resources/sap-ui-core.js"
data-sap-ui-theme="sap_belize" // 기본 테마 설정
data-sap-ui-libs="sap.m" // 라이브러리 설정
data-sap-ui-compatVersion="edge">
</script>
SAP UI5는 여러 부트스트랩을 제공하고 있다.
sap-ui-core.js : 기본 부트스트랩 파일이며, SAP에서 이 타입을 쓸 것을 추천한다. 이것은 이미 jquery 와 core library의 최소한의 요구를 이미 정의하고 있다. 파일 요구는 XHR 을 다양하게 로드된다.
Content Delivery Network (CDN) : CDN을 이용해서 다양한 라이브러리를 엑세스 할 수 있다.
sap-ui-core-nojQuery.js : SAP 제공 jquery가 아닌 다른 버전의 jquery를 실행할 때 사용한다.
sap/ui/core/library-preload.js : 미리 파싱해놓고 필요할 때 호출한다.
sap-ui-core-lean.js : jquery나 sap ui5 즉시 로드되고 다른 파일은 다이나믹하게 로드된다.
Components
SAPUI5 어플리케이션의 컴포넌트는 독립적이며, 부분 재 사용 가능하다.
SAP UI5는 어플리케이션이 동작하는 곳으로 부터 다른 위치의 컴포넌트 사용할 수 있다.
따라서 컴포넌트는 다른 팀과 협업적으로 사용할 수 있다.
캡슐화 지원으로 어플리케이션 구조와 코드를 더 쉽게 이해할 수 있다.
SAP UI5는 두 가지 컴포넌트 타입을 지원한다.
Faceless components (class: sap.ui.core.Component)
유저 인터페이스를 사용하지 않으며, UI 엘리먼트가 필요하지 않은 곳에서 코딩이 사용된다. faceless 컴포넌트들은 ComponentContainer에 추가할 수 없다.
UI components
Component들을 상속받으며 컴포넌트의 기술적인 렌더링을 추가한다. 유저인터페이스의 각각의 설정, 메타데이터에 따라 화면 영역이나 요소들을 나타낸다.
sap.ui.core.UIComponent는 sap.ui.core를 상속받으며 기능요소들을 렌더링한다.
컴포넌트는 sap.ui.component란 팩토리를 통해 생성되고 불러온다.컴포넌트들은 에 지정된 디스크립터(manifest.json)를 통해 불러올 수 있다. SAP는 디스크립터를 통해 컴포넌트 파일들을 호출하는 것을 추천한다. 프로세스가 로딩 중에 최적화 되어 퍼포먼스를 증가시키기 때문이다. 디스크립터는 메타데이터 요소들을 포함하며 또한 의존성과 설정에 대한 구성을 표현한다. 컴포넌트의 필수와 옵션 리소스는 구성요소의 네임스페이스에 조직적이여야 된다.
디스크립터를 로딩한 후에 컴포넌트 팩토리는 의존된 라이브러리나 컴포넌트들을 평행화 한 후 로드할 수 있다.
Component 구조
컴포넌트는 유니크한 네임스페이스로 조직적이며, 컴포넌트의 네임스페이스가 컴포넌트 이름과 동일하다.
기본적으로 컴포넌트는 컴포넌트컨트롤러와 디스크립터로 구성한다. 컴포넌트컨트롤러는 필수지만, SAP 는 디스크립터도 또한 사용하길 추천한다.
디스크립터는 컴포넌트 메타데이터를 포함하며, 또한 컴포넌트의 의존성, 설정을 나타낸다. 컴포넌트의 모든 구성요소들은 Component.js와 manifest.json이 나타낸다.
SAP UI5에서의 다른 개념
복합 제어
두 개념은 싱글 인터페이스 뒤에서 제어하는 걸 제공한다. 복합 제어는 컨트롤러 내에서 재사용을 목적으로 하며, 컴포넌트는 응용프로그램 개발을 재사용으로 하며 복합제어는 기존 제어를 포함한다.
UI 라이브러리
컨트롤 주위에 배포 가능한 단위이다. 컨트롤은 전혀 독립적이지 않고 제어 라이브러리의 부분으로서 배포된다. 그러나 컴포넌트는 자체 포함되므로, 컨트롤들을 배포하는데 사용하면 안된다.
MVC
MVC 개념은 뷰, 컨트롤러를 정의해 부품을 구조화하여 재사용되게 허락한다. MVC는 따로따로 배포되며, 스타일과 스크립트의 정의하는 것에 의미가 없기에 이 컨셉은 다른 어플리케이션에서의 사용을 제한한다.
MVC
SAP UI5 에서 model, View, Controller개념은 표현을 구분하며 사용자와 상호작용을 하기 위해 사용된다. 이 구분은 개발과 부분 독립적 변화에 용이하다.
view는 UI에 렌더링하는 일을 담당한다.
model은 어플리케이션 데이터를 관리한다.
controller는 view와 model에 대한 이벤트에 반응하고 변화를 상호작용한다.
UI에 안에 데이터를 바인딩 하는 목적은 사용자 인터페이스, 어플리케이션에 의한 데이터 노출, 데이터 프로세스에 의한 비즈니스 로직 코드에 대한 정의를 분리하기 위해서다. 분리를 통해서 가독성, 유지보수성, 확장성을 제공한다. 그리고 비즈니스 로직을 건들지 않고 view를 바꾸며 동일 데이터에 대해 여러 view를 분리한다.
View와 Controller는 1:1 매핑관계이다. 그러나 이것은 컨트롤러 단에서는 관계를 유지하지 않아도 된다. 이러한 컨트롤러는 어플리케이션 컨트롤러이다. 기술적 관점에서는 SAPUI5 모델을 제어하거나 상속할 수가 있다.
View와 Controller는 재사용이 가능하며 지원이 매우 좋다.
프로세스 초기화
SAP 안에 있는 JQuery 플러그인은 모듈화 개념, 로깅 프레임워크, 성능 측과 같은 기능을 제공한다.
global 오브젝트인 sap가 정의된다.
sap.ui.core.Core 클래스가 모든 의존성들과 함께 실행된다.
실행 설정이 다른 소스로부터 결정된다.
모든 라이브러리와 모듈 설정은 그들의 의존성들과 함께 호출된다.
테마설정으로 로드된 각각의 라이브러리와 css 파일이 설정된 테마를 로드한다.
모든 라이브러리와 문서가 로드되어 기다릴 때, core에 있는 initEvent 함수가 실행되며 모든 등록된 헨들러들이 실행된다.
초기화 준비
이를 통해 프레임 워크를 초기화 하며 3가지 방법으로 연결할 수 있다.
ComponentSupport 모듈
sap.ui.core.CommponentSupport를 사용하는 것이다. 프레임 워크를 초기화 한 후에 코드를 실행하는 것이다.
ComponentSupport를 통해 UIComponent를 선택하여 실행하는 방법이다.
ComponentSupport를 사용하면 필요한 ComponentContainer를 생성하여 하나 이상의 UIComponent를 정의하는 걸 허락한다.
Standalone data-sap-ui-oninit 모듈
모듈 외에 ComponentSupport 사용은, script 요소의 부트스트랩에서 init을 부를수 있는 데이터 요소로 정의할 수 있다.
oninit 모듈은 프레임워크의 프로세스가 초기화될 때의 타이밍에 로드되고 실행한다. 그 다음 우리는 모듈 안에서 추가적으로 어플리케이션을 실행할 수 있다. ( ex. 새로운 xml View 인스턴스 생성)
추가적으로 전용 init 모듈은 추가 <script> 인라인 태그가 필요하지 않으므로 요구사항에 따라 SAP 사이트에 게시된 CSP(Content Security Policy)를 반영할 수 있다.
attachInit 함수
attachInit 함수는 프레임워크가 초기화 된 후 바로 실행할 함수이다. 이 코드는 스크립트 인라인 태그를 나타내는 main HTML 파일안에 쓰여질 수 있다.
부트스트래핑중 추가 리스소 로딩
/<context-path>/resources/<library-name>/library.js
sap.ui.getCore().initLibrary 메서드를 호출하여 빌드 중 파일이 자동생성되게한다.
` //resources//themes//library.css`
css 또한 이 경로를 통해 실행한다.
라이브러리 동적 로드
sap.ui.getCore().loadLibrary() 메서드를 통해 런타임에 라이브러리를 로드한다.
이렇게 동적으로 라이브러리를 추가했을 땐, document.ready 이벤트 후에 엑세스가 가능하다.
SAP UI5 구성도
manifest.json
sap에선 이 파일을 app 세팅 설정이나 app이 실행되기에 필요한 중요 정보를 모아놓으라고 추천한다.
이러한 접근을 사용하는 것은 어플리케이션 코드를 작성하기 필요한 셋팅을 미리 준비하거나, manifest.json에 미리 인스턴스화하여 앱 실행 전에 미리 정보를 엑세스 할 수 있다.
몇몇 속성은 각각 SAPUI5 버전에 필요 정보의 목적이며, 외부 구성요소와 어플리케이션을 정확하게 통합하지만, 대부분의 속성들은 실제로 최소한의 UI5 version에 필요하도록 설정이 사용된다.
Root View (App.view.xml)
App.view.xml는 app에서의 root view를 정의한다. 대부분의 경우 App Control 또는 root control로서의 SplitApp control에 포함한다.
SAP UI5는 다양한 뷰타입들을 지원한다. 컨트롤러 파일 안에 정의된 View로부터 컨트롤러 로직을 작동하기 위해 SAP에선 XML을 추천한다. ex) App.controller.js
Component.js
Component.js 파일은 앱 설정을 위한 파일이며 컴포넌트가 인스턴스화 되었을 때 SAPUI5에서 자동으로 init 함수가 실행된다.
(우리가 생성하는 Component는 UIComponent에 상속되어있으며, 만약 init을 오버라이딩 하겠다면, 너는 라우터의 초기화와 UIComponent의 init 함수가 확실히 불려져야 한다.)
컴포넌트의 metadata 섹션에서는, 디스크립터 참조를 정의하며, 컴포넌트가 인스턴스화 되었을 때, 이 디스크립터는 자동으로 로드된다.
HTML Page
App for FLP
모든 앱은 SAP UI5 그리고 그 요소를 사용하는 HTML 페이지로 시작된다. 그리고 이는 FLP 와 standalone app 라는 2가지 옵션이 있다.
FLP는 디스크립터 파일에서 주어진 정보를 기반으로 요소들을 인스턴스화한다. FLP는 같은 시간에 여러 앱들을 포함하며, 각각의 앱들은 각자의 테마와 지원장치들을 각각 로컬 셋팅으로 정의할 수 있다.
Standalone app
standalone을 실행하길 원하면 component.index.html을 인스턴스한 파일을 생성하는 것이 필요하다.
순서
SAP UI5 Tutorial 참조 사이트 (hello world 호출 까지의 과정))
ui5 serve 명령어를 실행하면 제일 먼저 index.html 파일을 로드한다.
1. index.html
(html content ...)
<script
id="sap-ui-bootstrap"
src="https://openui5.hana.ondemand.com/resources/sap-ui-core.js"
data-sap-ui-theme="sap_belize"
data-sap-ui-libs="sap.m"
data-sap-ui-compatVersion="edge"
data-sap-ui-async="true"
data-sap-ui-resourceroots='{"sap.ui.demo.walkthrough": "./"}'
data-sap-ui-oninit="module:sap/ui/demo/walkthrough/index">
</script>
(html contents ...)
<body class="sapUiBody" id="content">
(html contents ...)
index.html에 설정 해 놓은 <script> 태그 내 실행, 속성 값
src : SAP UI5 에서 지원하는 CDN 경로 호출
theme : ui에 사용할 테마 설정
libs : 기본으로 사용할 라이브러리 설정
compatVersion : 호환성 정의
async : 비동기 설정 유무
resource-root : 네임스페이스 경로 alias 설정
oninit : 첫 시작 모듈 설정
data-sap-ui-resourceroots='{"sap.ui.demo.walkthrough": "./"}'
data-sap-ui-oninit="module:sap/ui/demo/walkthrough/index
sap/ui/demo/walkthrough 경로를 ./ 로 설정하였고,
oninit 속성에 따라 페이지 실행 시 현재 파일 경로에 있는 index.js 파일을 실행한다.
2. index.js
sap.ui.define([
"sap/ui/core/mvc/XMLView"
], function(XMLView) {
'use strict';
XMLView.create({
viewName: "sap.ui.demo.walkthrough.view.App"
}).then(function(oView) {
oView.placeAt("content");
});
});
);
sap.ui.define을 통해 호출 시, 정의할 내용을 입력한다.
sap/ui/core/mvc/XMLView는 core.js에서 지원하는 XMLView 타입을 선언하고
함수를 통해 XMLView를 생성하게 한다.
create 함수로 viewName 을 정의하며 sap.ui.demo.walkthrough.view.App 경로에 정의한 App 이란 이름의 빈 xml을 생성한다.
3. App.View.xml
<mvc:View
controllerName="sap.ui.demo.walkthrough.controller.App"
xmlns="sap.m"
xmlns:mvc="sap.ui.core.mvc">
<Button
text="click"
press=".onShowHello"/>
</mvc:View>
View와 Controller는 1:1 매핑이므로 controllerName으로 App.controller.js를 매핑하며
App.view.xml이 페이지에 로드되었을 때 App.controller.js 가 인스턴스화된다.
4. App.Controller.js
sap.ui.define([
"sap/ui/core/mvc/Controller",
.
. 정의 타입들
.
], function ( 정의 타입들) {
"use strict"; // 코드 검사를 강화시켜 스크립트 오류 방지
return Controller.extend("sap.ui.demo.walkthrough.controller.App", {
onInit : function () {
... Controller 호출 시 실행될 코드
},
onShowHello : function () {
MessageToast.show("Hello World");
}
});
});
Controller에서도 각종 sap 지원 기능 태그들을 정의해주며
함수를 통해 Controller.extend 를 호출하면서 각종 XML 출력에 필요한 로직들을 진행한다.
생각
잠깐 동안 UI5 만졌을 땐, 현 웹에서 사용하는 구조와 크게과 다르지 않았으며, 눈에띄는 차이는 view에서 xml을 호출할 때 Controller를 인스턴스화 하는 방식에 조금 신기했었다. hello world 출력을 기반으로 아직은 장단점에 대해 크게 파악하기 어려웠다,
제가 생각하는 장점에선 SAP UI5가 고퀄리티 프레임워크라 느꼈던 것이 각 하나하나의 View의 태그 생성하는 과정에 대해 디테일한 설정을 가져갈 수 있다는 것과 프론트 단 부분을 sap.m 같은 라이브러리를 통해 대부분 구성해 놓은 것을 사용할 수 있다는 점에서 UI5를 만들 때 신경을 많이 두었다는 느낌이 들었다.
또한 설정에 대한 내용을 디스크립터에 넣는 방식으로 일일히 설정에 대해 파고 들어갈 이유가 없었던 것도 좋은 방식이라 생각한다.
허나 sap로 만든 사이트중 하나를 들어가 봤는데 페이지 로딩 속도가 느렸다. 제가 생각하는 이유는 UI5도 하나의 프레임워크 방식이기에 페이지 로딩 방식이 고정되어 있어, javascript의 DOM을 BOM을 받아 파싱해서 쉽고 빠르게 화면에 뿌려주는 것을 생각하면 템플릿에 맞춰야 하는 상황이 느릴 수도 있다 생각한다. 또한 View 타입의 형태에 맞춰 Controller를 진행하는 과정이 끝날 때까지 기다려서 출력하는 구조라고 생각한다. 그렇기 에 때문에 대표님께서 말씀하신 NextJs 같이 SSR을 통해 템플릿을 프리로딩하고 이후 CSR을 진행하는 방식을 접했을 때 어느 정도까지의 퍼포먼스를 보여줄 수 있을 지 궁금하다.
-
OData V2 Model
oData V2 model
oData V2 모델을 사용하면 oData Service로 부터 데이터 제어를 가져올 수 있다.
oData V2 모델은 서버측의 모델이며 데이터 셋은 단지 서버에서 사용가능하고 클라이언트들은 단순히 요청된 데이터만 알 수 있다. 각 정렬과 필터링 작동은 서버 위에 올라간다. 클라이언트는 서버에 요청을 보내고 응답한 데이터를 보인다.
백앤드 요청은 바인딩 목록, 컨텍스트 바인딩들, 그리고 CRUD 함수들이 ODataModel에 의해 제공된다. 속성 바인딩은 요청이 되지 않는다.
OData model은 현재 2.0 버전까지 지원한다.
2가지 버전의 OData 모델은 sap.ui.model.odata.ODataModel 과 sap.ui.model.odata.v2.ODataModel을 참조한다. v2.ODataModel은 기능이 더해졌고, 참조된 이 모델 안에서 새로운 기능을 참조만 할 것이다. sap.ui.model.odata.ODataModel은 지원되지 않으며 SAP는 v2.ODataModel 만 사용할 것을 추천한다.
기능
v2.ODataModel
ODataModel
OData version 지원
2.0
2.0
JSON format
Yes
Yes
XML format
Yes
Yes
양방향 바인딩 지원
속성 변경만 가능
하나의 엔티티 속성만 동시변경 가능
기본 바인딩
단방향
단방향
클라이언트 사이드 정렬, 필터링
Yes
NO
batch
모든 요청들은 배치될 수 있다.
단지 메뉴얼 배치 요청만 가능
데이터 캐시
모델 안의 모든 데이터가 캐시된다.
메뉴얼적 요청된 데이터는 캐시되지 않는다.
자동 새로고침
yes
yes
메세지 핸들링
yes
no
다른 도메인이나 사이트에서의 백앤드가 엑세스 하는 것을 막는 Same-Origin-Policy 보안개념을 유의해야한다.
패치 데이터의 서비스 요청들은 데이터 바인딩을 제어를 위해 정의를 기반으로하여 자동으로 만들어졌다.
ODataModel 생성자
new sap.ui.model.odata.v2.ODataModel(vServiceUrl, mParameters?)
vServiceUrl : 추가된 URL 파라미터들은 모든 요청을 덧붙일 것이다.
만약 오브젝트를 전달하고 싶을 땐, 이것은 두번째 파라미터 오브젝트로써 해석될 것이다. 그땐 mParameter.serviceUrl은 필수 파라미터가 된다.
모델 인스턴스 생성
하나의 OData 모델 인스턴스는 단지 하나의 OData service를 커버할 수 있다.
다양한 서비스를 엑세스 하기 위해선 다양한 OData Model을 인스턴스 해야한다.
// "ODataModel" required from module "sap/ui/model/odata/v2/ODataModel"
var oModel = new ODataModel("http://services.odata.org/Northwind/Northwind.svc/");
var oModel = new ODataModel({serviceUrl: "http://services.odata.org/Northwind/Northwind.svc"});
ODataModel 인스턴스를 생성할 때 서비스 메타데이터를 검색하라는 요청이 전송된다.
Service Metadata
서비스 메타데이터는 서비스 URL 별로 캐싱된다. 같은 서비스에서 사용하는 여러 OData 모델은 이 메타데이터를 공유할 수 있다.
첫번째 모델 인스턴스만 메타데이터 요청을 작동한다. JSON 서비스 메타데이터 응답은 getServiceMetadata() 라는 OData 모델 인스턴스의 메소드를 호출함으로 엑세스 될 수 있다.
var oMetadata = oModel.getServiceMetadata();
v2.ODataModel 안에서 서비스 메타데이터는 비동기식으로 불러지며 동기식으로 불러올 수 없다. 로딩이 끝났을 때, metadataLoaded 이벤트가 붙는다.
추가 URL 파라미터
OData Service는, URL 파라미터를 설정해서 사용할 수 있다. UI5는 각각의 바인딩에 따라서 파라미터들을 자동적으로 설정한다.
인증토큰이나 일반 설정 옵션들 같은 경우엔 요청 URL에 요소들을 추가할 수 있다.
몇몇 파라미터들은 모든 요청에 포함되선 안되며, $expand 또는 $select와 같은 특정 목록이나 컨텍스트 바인딩에 추가되어야한다. 바인딩 메서드는 특별한 바인딩을 위한 모든 요청을 포함할 수 있는 파라미터 맵을 전달하는 옵션을 제공한다. OData 모델은 현재 $expand와 $select를 지원한다.
URL 파라미터들을 요청하는데 2가지 방법이 있다.
파라미터들을 service URL에 추가
new ODataModel("http://myserver/MyService.svc/?myParam=value&myParam2=value");
이 파라미터들은 OData 서버에 보내지는 모든 요청이 포함될 것이다.
mparameter map과 함께 URL 파라미터 전달
$metadata 요청(metadataUrlParams)이 사용되며 게다가 데이터 요청에 포함된 URL 파라미터(serviceUrlParams)를 전달할 수 있다. 파라미터들은 Map 형식으로 보내진다.
// "ODataModel" required from module "sap/ui/model/odata/v2/ODataModel"
var oModel = new ODataModel({
serviceUrl: "http://services.odata.org/Northwind/Northwind.svc",
serviceUrlParams: {
myParam: "value1",
myParam2: "value2"
},
metadataUrlParams: {
myParam: "value1",
myParam2: "value2"
}
});
HTTP HEADER
각 요청에 보내어지는 커스텀 헤더를 추가할 수 있다.
맵 파라미터와 함꼐 커스텀 헤더로 전달
var oModel = new sap.ui.model.odata.v2.ODataModel({
headers: {
"myHeader1" : "value1",
"myHeader2" : "value2"
}
});
모델 인스턴스를 글로벌 커스템헤더로 셋팅
oModel.setHeaders({"myHeader1" : "value1", "myHeader2" : "value2"});
커스텀 헤더 추가 시 이전 모든 커스텀 헤더들은 지워진다. 일부 헤더들은 private이며 즉, OData모델 내부적으로 설정되어 설정할 수 없다.
엔티티 주소 : 바인딩 경로 구문
OData 모델의 바인딩 경로 구문은 특정 엔티티들이나 엔티티 집합을 엑세스한 OData 안의 사용되는 서비스 URL과 상대적 URL 경로와 일치한다.
서비스의 메타데이터 안에 정의된 OData 서비스의 구조에 따른 OData에 의해 제공되어진다.
필터들과 같은 URL 파라미터들은 바인딩 경로를 추가할 수 없다. 바인딩 경로는 절대적이거나 상대적이다. 절대적 바인딩 경로는 즉시 리졸브 되지만, 상대경로는 절대경로에서 절대적 바인딩 경로를 자동적으로 전환될 수 있을 때 리졸브 될 수 있다. 만약 예를 들어 속성이 상대 경로에 바인딩 되고 그 부모 제어가 절대적 경로에 바운드 된다면, 상대적 경로는 절대적 경로를 통해 해결될 수 있다.
OData Model 내 바인딩 샘플은 Northwind 데모 서비스에서 부터 가져온다.
절대적 바인딩 경로
"/Customers"
"/Customers('ALFKI')/Address"
컨텍스트에서 확인할 수 있는 상대적 바인딩 경로
"CompanyName"
"Address"
"Orders"
전체 경로
"/Customer('ALFKI')/CompanyName"
"/Customer('ALFKI')/Address"
"/Customer('ALFKI')/Orders"
단일 엔티티 또는 엔티티들의 컬렉션이 사용되는 탐색 속성
"/Customers('ALFKI')/Orders"
"/Products(1)/Supplier"
OData Model로부터 데이터 엑세스
OData 서비스에서 요청한 데이터는 OData 모델에 캐시된다. getProperty() 메서드에 의해 엑세스 될 수 있으며, 엔티티 객체 또는 값을 리턴한다. 이 메서드는 백앤드로부터 요청한 데이터가 아니며, 단지 미리 요청되거나 캐시된 엔티티들로 엑세스 될 수 있다.
oModel.getProperty("/Customer('ALFKI')/Address")
이 메소드들로 싱글엔티티들과 속성들을 엑세스 할 수 있다. 엔티티 집합들을 엑세스하면, 바인딩 목록으로 읽기 전용 엔티티들을 바인딩 컨텍스트로 가져올 수 있다. 이 메서드가 리턴한 값들은 JSON 모델 안에서 참조된 것이 아닌 카피된 데이터이다.
엔티티 생성
createEntity() 메서드를 호출하면 명시된 엔티티 집합에 대한 엔티티들을 호출한다. 이 메서드는 새로 만들어진 엔티티를 가리키는 컨텍스트 객체를 반환한다.
이 어플리케이션은 이러한 객체들을 바인딩 할 수 있고 양뱡향으로 데이터들을 변경할 수 있다. submitChanges() 라고 불리는 메서드를 호출하면, OData 백앤드 안에 엔티티들을 저장한다. 변경을 초기화하려면 deleteCreateEntry() 함수를 부르면 된다.
var oContext = oModel.createEntry(
"/Products",
{ properties:
{ ID:99,
Name:"Product",
Description:"new Product",
ReleaseDate:new Date(),
Price:"10.1",
Rating:1
}
}
); // 명시된 속성들과 값을 가진 제품들의 엔티티 생성
oForm.setBindingContext(oContext); // 엔티티 바인딩
oModel.submitChanges({success: mySuccessHandler, error: myErrorHandler}); // Odata 백앤드 안에 엔티티 저장
entity oModel.deleteCreatedEntry(oContext); // 변경 취소 시 호출
만약 엔티티들을 submit 하면 컨텍스트는 모델에 import 된 새로운 데이터를 모델로 가져온다. 새로운 엔티티를 가리킨다.
CRUD 작동
OData 모델은 OData Service에서 메뉴얼적인 작업을 허용한다. 메뉴얼적 작업이 데이터를 반환할 때는 Odata 모델 데이터의 캐시가 import 된다. 모든 작업들은 필수적 sPath 파라미터와 mParameters Map을 선택적으로 요구한다.
또한, 생성, 수정 메소드들은 데이터 객체가 생성되거나 변경될 때 OData 파라미터를 필수적으로 요구한다. 각각의 작업은 요청 중단을 사용할 수 있는 중단함수를 포함한 객체를 반환한다. 만약 요청이 중단되어 에러 핸들러가 호출된다면, 이것은 성공 또는 에러 핸들러가 모든 요청에 대해 호출되도록 보장한다. 이것은 또한 헤더 데이터, URL 파라미터, 또는 eTag에 대해 추가적으로 보낼 수 있도록 한다.
엔티티들 생성
create 함수는 OData 모델의 생성이 명시된 OData service의 POST 요청을 작동시킨다. 어플리케이션은 새로운 엔티티와 생성된 엔티티 데이터를 엔티티 셋으로 가진다.
var oData = { ProductId: 999, ProductName: "myProduct" } // 엔티티 데이터
oModel.create("/Products", oData, {success: mySuccessHandler, error: myErrorHandler}); // 엔티티들을 create()를 통해 명시된 모델로 생성한다.
엔티티 읽기
read 함수는 GET 요청으로 작동된다. 이 경로는 OData model에 의해 생성에서 명시된 OData service로 부터 가져온다. 이 가져온 데이터는 성공여부에 따라 success handler와 error handler로 나뉜다.
oModel.read("/Products(999)", {success: mySuccessHandler, error: myErrorHandler});
엔티티 수정
update 함수는 PUT/MERGE 요청으로 작동하며 성공적으로 요청이 된 후에는 model 안에서 refresh 된 데이터를 자동으로 가져온다.
oModel.update("/Products(999)", oData, {success: mySuccessHandler, error: myErrorHandler});
엔티티 삭제
delete 함수는 DELETE 요청으로 작동하며, 어플리케이션에서는 삭제해야할 엔트리의 경로를 명시해야한다.
oModel.remove("/Products(999)", {success: mySuccessHandler, error: myErrorHandler});
변경 후 refresh
모델은 변화된 엔티티들을 의존하여 자동적으로 refresh를 바인딩하는 메커니즘을 제공한다. 만약 create, update, remove 함수를 수행하면, 그 고유의 모델은 바인딩을 확인하며 이를 위해 refresh를 작동한다. 만약 그 모델이 배치모드에서 실행되면, 새로고친 요청은 같은 배치 요청 안의 변화들과 함께 묶이게 된다. setRefreshAfterChange(false) 호출에 의해 자동으로 비활성화 할 수 있다. 만약 자동 고침이 비활성화 되었을 때, 어플리케이션은 각각의 바인딩들을 새로고쳐야 한다.
자식 엔티티 생성
부모 엔티티에 새로운 자식 엔티티 생성은 한번의 싱글 API 요청으로 가능하지 않다. 이를 만들려면 부모, 자식의 생성 엔티티 API 호출을 연결하면 만들 수 있고 아래의 샘플은 판매 주문과 주문 항목을 모두 생성한다.
var oParentContext, oModel = this.getView().getModel();
oParentContext = oModel.createEntry("SalesOrderSet", { //부모 엔티티 생성
properties : {
// 새로운 판매 주문을 위한 속성
},
success : function () { // 부모(oParentContext)가 생성되었을 때
oChildContext = oModel.createEntry("ToLineItems", { //자식 엔티티 생성
context : oParentContext, // context에 부모의 컨텍스트 저장
properties : {
// 새로운 판매 주문에 의한 새로운 아이템들의 속성
},
success : function () {
// ...
}
});
oModel.submitChanges(); // OData 백앤드에 저장
}
});
oModel.submitChanges(); // OData 백앤드에 저장
동시성 제어 와 ETags
OData는 optimistic한 동시성 제어를 HTTP ETags를 사용한다. ETag는 모든 CRUD 요청을 위해서 parameter map으로 전달될 수 있다. 만약 ETag가 전달되지 않으면, 미리 로드된 경우, 캐시된 엔티티가 사용된다.
XSRF Token
CSRF 를 해결하기 위해 OData 서비스는 데이터 변경 시 XSRF 토큰을 필요로 한다. 이 경우 서버에서 가져온 토큰을 같이 서버에 보내야 하며 OData 모델은 메타데이터를 읽을때, XSRF 토큰을 가져와 header에 넣어 자동으로 토큰을 전송한다. 토큰이 유효하지 않을때엔, OData Model에서 refreshToken 함수를 호출하여 패치될 수 있다. 이 토큰은 루트 URL 서비스의 호출과 함께 가져오며 서비스 다큐먼트와 함께 응답한다. 유효한 토큰을 가져오려면 응답이 캐싱되지 않았는지 확인해야한다.
모델의 새로고침
refresh 함수는 OData 모델의 모든 데이터를 새로고침한다. 각각의 바인딩은 서버로부터 데이터를 다시 읽는다. 리스트 또는 컨텍스트 바인딩 경우, 백앤드에서 새로운 요청이 작동된다. 만약 XSRF 토큰이 유효하지 않으면, 서비스 다큐먼트에서 읽기 요청을 다시 가져와야 한다. 메뉴얼 CRUD 요청들로 임포트된 데이터는 자동으로 리로드 되지 않는다.
배치 프로세싱
ODataModel v2는 2가지 다른 방법으로 batch processing을 지원한다.
기본 : 쓰레드안에 모든 요청들은 배치 요청들로 수집되고 다뤄진다. 즉, 요청은 현재 스택이 종료된 후에 타임아웃 요청을 보낸다. 이것은 바인딩에 의해 작동된 요청들로부터 모든 메뉴얼적 CRUD 요청들을 포함한다.
지연 : 요청들은 저장되며 어플리케이션의 의해 호출되는 submitChanges() 과 함께 submit 되어질 수 있다. 이것은 또한 바인딩으로 부터 요청들이 작동될 뿐만 아니라, 모든 메뉴얼적 CRUD 요청들이 포함된다.
모델이 요청을 다루는 방법은 결정할 수 없다. 그래서 SAPUI5는 groupId를 제공한다. 각각의 컨텍스트와 바인딩 리스트 그리고 각각 메뉴얼 요청들에 그룹 아이디를 지정할 수 있다. 같은 그룹안에 속하는 모든 요청들은 하나의 요청 안에서 다뤄질 수 있다. 그룹아이디가 없는 요청은 기본 배치 그룹 안에서 다뤄진다. 변경을 위해 chageSetId 를 사용할 수 있다. 같은 changeSetId에 포함된 각 변경들은 배치 요청안에서 하나의 changeSet으로 다뤄진다. 기본적으로, 모든 변화들은 고유의 changeSet을 가진다.
setDefiredGroup() 메서드를 사용하여 이전에 정의된 그룹의 서브셋을 지연으로 설정할 수 있다. (setChangeGroups() , getChangeGroups() 에도 동일 시 적용)
그룹에 속한 모든 요청들은 요청 queue안에 저장된다.
지연된 배치 그룹은 submitChanges() 메소드를 통해 메뉴얼적으로 전송되어야한다. 만약 submitChanges()를 호출할 때 배치 그룹아이디를 명시하지 않으면, 모든 지연된 배치 그룹들이 전송된다.
- 그룹들의 서브셋을 지연으로 설정
var oModel = new ODataModel(myServiceUrl);
- 바인딩한 groupId를 전송한다.
{path:"/myEntities", parameters: {groupId: "myId"}}
- 지연한 groupId 설정
1. 그룹 가져오기
var aDeferredGroups = oModel.getDeferredGroups();
2. 리스트에 그룹 아이디 추가
aDeferredGroups = aaDeferredGroups.concat(["myId"]);
3. 지연된 모든 그룹들을 설정
oModel.setDeferredGroups(aDeferredGroups);
4. 지연된 모든 그룹 전송
oModel.submitChanges({success: mySuccessHandler, error: myErrorHandler});
양방향 바인딩
v2.oDataModel은 양방향 바인딩을 지원한다. 기본적으로 모든 변경들은 지연으로 설정된 changes 라는 배치그룹에 수집된다.
changes 로 전송하려면, submitChanges() 함수를 사용한다. changes 데이터는 복사 데이터로 만들어진다. 이것은 이전 데이터를 가져와 백앤드에 새 요청을 보내지 않고도 changes를 재설정 할 수 있다. resetChanges() 써서 모든 changes 저장공간을 리셋할 수 있다. 또한 엔티티 경로 배열이 있는 resetChanges() 함수를 호출하여 특정 엔티티만 리셋할 수 있다.
model에서 setChangeGroups() 를 사용하면, 다른 배치 그룹안에 있는 다른 엔티티들 또는 타입들을 위한 changes도 수집할 수 있다.
var oModel = new ODataModel(myServiceUrl);
oModel.setDeferredGroups(["myGroupId", "myGroupId2"]);
oModel.setChangeGroups({
"EntityTypeName": {
groupId: "myGroupId", //
changeSetId: "ID", // 선택사항 (Id 변경)
single: true, /*optional, can be true or false*/
}
});
oModel.submitChanges({groupId: "myGroupId", success: mySuccessHandler, error: myErrorHandler});
같은 배치 그룹 안에서 모든 엔티티 타입을 위한 changes 수집은 ‘*‘를 EntityType으로 사용한다. 만약 change가 지연으로 설정하지 않으면, changes는 즉시 백앤드에 보내어진다. 단일 파라미터를 true 또는 false로 설정에 의해 , 고유의 change 설정 안에 각각의 change 결과들이거나 changes가 하나의 change 설정을 수집할 것인지를 정의한다. 모델은 단지 single의 설정이 false 일 때만 changeSetId를 처리한다.
var oModel = new ODataModel(myServiceUrl);
oModel.setProperty("/myEntity(0)", oValue);
oModel.resetChanges(["/myEntity(0)"]);
바인딩별 매개변수
OData 프로토콜은 명시적으로 다른 URL 파라미터이다.
위에서 설명한 파라미터들 뿐만 아니라 바인딩 안에 파라미터들을 사용할 수 있다.
Expand parameter
확장 파라미터는 어플리케이션에서 그들의 네비게이션 속성들과 함께 연결된 엔티티들을 읽는 걸 허락한다.
oControl.bindElement("/Category(1)", {expand: "Products"});
oTable.bindRows({
path: "/Products",
parameters: {expand: "Category"}
});
예로 Category(1) 의 모든 제품들은 서버 응답안에 inline으로 내장되어 하나의 요청만 부른다. 모든 Products 위한 카테고리는 각각의 product 위한 응답에 inline으로 내장되어있다.
Select parameter
선택 파라미터는 어플리케이션이 엔티티를 요청할 때 읽는 속성들의 서브셋으로 정의하는 것을 허락한다.
oControl.bindElement("/Category(1)", {expand: "Products", select: "Name,ID,Products"});
oTable.bindRows({ path: "/Products", parameters: {select: "Name,Category"} });
예를 들어 Name, ID 그리고 ofCategory(1) 속성 뿐만 아니라 제품에 내장된 모든 속성들도 응답한다. 이 속성과 카테고리는 각각의 제품에 포함되며 카테고리 속성은 관련된 카테고리 항목에 대한 링크를 포함한다.
Custom query options
서비스 작동을 위한 input 파라미터로서 커스텀 쿼리 옵션들을 사용할 수 있다. list 바인딩이 생성될 때, 커스텀 파라미터를 명시한다.
oTable.bindRows({
path: "/Products",
parameters: {
custom: { // 커스텀 파라미터 명시
param1: "value1",
param2: "value2"
}
},
template: rowTemplate
});
만약 bindElement()를 사용하면, 아래와 같이 파라미터를 명시할 수 있다.
oTextField.bindElement("/GetProducts", {
custom: {
"price" : "500"
}
});
바인딩 의존 최적화
OData V2 모델 에서 생성자는 preliminaryContext 라 불리는 flag를 지원한다. 이 옵션이 true 이면, 모델은 더 적은 $batch 요청 종속적 바인딩에 대한 OData 호출을 다를 수 있게 된다.
소개
부모 바인딩이 의존 바인딩에 설정된 컨텍스트에 해당하는 OData 엔티티를 읽는다면 다른 바인딩(부모바인딩) 에 의존한다.
기본적으로 종속 바인딩을 위한 데이터는 바인딩 컨텍스트를 위한 데이터를 부모 바인딩를 통해 읽은 후에만 읽는다.
부모 바인딩이 컨텍스트 바인딩인 경우, 2개의 읽기 요청을 하나의 번들링으로 퍼포먼스를 증가시킬 수 있다. 바인딩과 연결된 단일 컨텍스트는 예비의 컨텍스트를 명시하여 수행할 수 있다. 이를 위해 부모 바인딩 구성에 createPreliminaryContext 파라미터 설정이 필요하다. 의존적 리스트 또는 컨텍스트 바인딩은 데이터를 읽기 전에 preliminaryContext 경로를 사용하여 데이터를 읽어 그들 자신의 요청을 위한 경로 구성을 주문 할 수 있다. 의존 바인딩의 생성을 usePreliminaryContext 파라미터를 설정해 수행한다.
설정과 사용
OData V2 model을 생성할 떄 preliminaryContext 파라미터를 설정할 수있다. 이 모델을 위해 생성된 모든 바인딩의 preliminary 컨텍스트를 전환한다.
모든 컨텍스트 바인딩은 createPreliminaryContext 파라미터를 true로 가진다.
모든 컨텍스트 바인딩과 모든 바인딩 목록은 usePreliminaryContext 파라미터를 true로 설정한다.
ODataContextBinding 생성자 또는 ODataListBinding 생성자에 해당하는 파라미터들의 기본을 무시할 수 있다.
게다가 모델에 일반적 preliminaryContext 파라미터를 사용하지 않을 수도 있다. (모든 바인딩에 영향을 미친다)
그러나 이러한 매개변수 사용하는 바인딩 인스턴스와 부모쌍에 대한 preliminaryContext를 바꾼다.
예를 들면, 부모 바인딩인 “/Product{1}” 경로를 가진 컨텍스트 바인딩을 보여준다.(sap.m.Panel 경로로 바인딩된 컨텍스트들을 생성) ‘Supplier’ 경로를 가진 의존적 관련 바인딩은 product의 모든 supplier를 보여주는 테이블로 생성된다. ( sap.ui.table.Table 제어 위한 행 을 생성)
싱글 바인딩 예제
표에 따르면, preliminaryContext들을 사용하지 않고, Binding 0 위한 하나의 요청, 그리고 나중에 Binding 1 위한 하나, 두개 연속으로 OData 요청들이 이슈가 된다.
요청 넘버
요소
1
GET Product(1)
1
GET Product(1)/Supplier
아래에서 보여주는 바인딩 매개변수를 설정하여 요청을 최적화 할 수 있다.
싱글 바인딩 예제 최적화
여기 Binding 1은 Binding 0이 생성될 때 preliminaryContext를 사용하며, 그리고 이와같이 요청 URL 은 다이렉트로 해결될 수 있다.
| 요청 넘버 | 요소 |
|:–:|:–:|
|1| GET Product(1),GET Product(1)/Supplier|
함수 임포트
ODataModel은 callFunction() 메소드에 의해 임포트나 또는 액션 함수들의 부르는 것을 지원한다.
oModel.callFunction("/GetProductsByRating",{method:"GET", urlParameters:{"rating":3}, success:fnSuccess, error: fnError})
만약 callFunction() 요청이 지연되면, submitChangesmethod()를 통해 전송되어 질 수 있다.
파라미터를 가져오는 함수의 바인딩
OData Model V2는 파라미터들을 가져오는 함수에 대해 바인딩을 지원한다. 엔티티 속성에 대해 바인딩을 지원하는 createEntry() 메서드와 비슷하다. callFunction() 메서드는 promise를 가진 핸들을 반환한다. 그 promise는 바인딩된 컨텍스트가 성공적으로 생성되거나 또는 거부될 때 해결된다.
var oHandle = oModel.callFunction("/GetProductsByRating", {urlParameters: {rating:3}});
oHandle.contextCreated().then(function(oContext) {
oView.setBindingContext(oContext);
});
만약 함수가 결과 데이터를 가져오면, 그때 그 결과 데이터는 엑세스 되고 컨텍스트 사용 속성 $result 에 대해 묶인다.
<form:SimpleForm>
<core:Title text="Parameters" />
<Label text="Rating" />
<Input value="{rating}" />
<Button text="Submit" press=".submit" />
<core:Title text="Result" />
<List items="{$result}">
<StandardListItem title="{Name}" />
</List>
</form:SimpleForm>
언어
SAPUI5는 현재 언어의 컨셉으로 사용한다. 이 언어는 자동적으로 OData V2 model에 의해 OData service로 전달된다. 이 이유로 어플리케이션은 언어 자체를 하드 코드로 해서 안된다. 예로 커스텀 쿼리 옵션에 ‘sap-language’ URL 파라미터를 명시해서는 안된다.
OData V2위한 메타 모델
sap.ui.model.odata.ODataMetaModel의 구현은 OData V2 metadata, 그리고 V4 어노테이션 둘다 통일된 엑세스로 제공한다.
메타모델은 기존 sap.ui.model.odata.ODataMetadata 기반으로 사용하며 엔티티나 또는 속성에 직접적으로 상승하는 기존 sap.ui.model.odata.ODataAnnotations로부터의 OData Version 4.0을 병합한다.
기본 구조
sap.ui.model.odata.ODataMetadata 의 기본 구조는 코드 스니핑에 따라서 보여진다. 이것은 엔티티 모델이 중첩되는 가장 중요한 요소가 어떻게 되는지 보여준다.
각각의 요소들은 약간의 네임스페이스로 부터 XML 속성 값의 확장성을 가질 수 있다. 아래 코드스니핑은 이러한 확장성들이 어떻게 저장되고 처리되는 지를 보여준다.
"dataServices": {
"schema": [{
"association": [{
"end": []
}],
"complexType": [{
"property": []
}],
"entityContainer": [{
"associationSet": [{
"end": []
}],
"entitySet": [],
"functionImport": [{
"parameter": []
}]
}],
"entityType": [{
"property": [],
"navigationProperty": []
}]
}]
}
객체, 속성 엑세스
OData 메타 모델 안에 객체들은 배열로 처리된다. 예로 ‘/dataService/schema’는 각각 스키마가 엔티티 타입, 등등의 배열과 함께 엔티티 타입 속성을 가지는 스키마들의 배열이다. 그래서 ‘/dataServices/schema/0/entityType/16’은 ‘MySchema’ 네임스페이스가 스키마 내 이름이 ‘Order’인 엔티티 타입의 경로가 될 수 있다.
그러나 이 경로들은 안정적이지 않다. 만약 낮은 인덱스를 가진 엔티티 타입이 스키마로부터 제거되었을 때, ‘Order’ 경로는 ‘/dataServices/schema/0/entityType/15/’ 로 변한다. index가 변하는 문제를 피하기 위해서 getObject 나 getProperty는 인덱스를 위한, XPath 같은 쿼리를 지원한다. 각각의 인덱스는 대괄호 안 쿼리에 의해 대체될 수 있다.
예를 들어
경로를 사용하는 스키마 주소
/dataServices/schema/[${namespace}==='MySchema']
경로를 사용하는 엔티티 주소
/dataServices/schema/[${namespace}==='MySchema']/entityType/[${name}==='Order']
를 지정하여 사용할 수 있다.
대괄호 안의 구문은 바인딩 구문의 표현식이랑 일치한다. 쿼리는 결과가 true로 될 때까지 각 객체들을 위해 실행된다. 그 다음 객체가 선택되면
이 경로를 복잡한 바인딩 구문을 사용하여 바인딩 식에 끼워넣는다.
`${path:'...'}`.
Example: `{:= ${path:'target>extensions/[${name} === \'semantics\']/value'} === 'email'}`
이러한 각 쿼리들은 자체적으로 포함되어있다. 쿼리는 관련 경로를 통해서 현재 후보의 속성에 언급할 수 있다. 그러나 XML 탬플릿 안에서 이용가능한 ${meta>} 와 같은 변수들은 언급할 수 없다.
확장자
확장 배열과 SAP 사용해 단순 속성의 객체로 변환: 이전에 속성 이름들을 추가 ex) 8번쨰 라인 참조
1 {
2 "name": "BusinessPartnerID",
3 "extensions": [{
4 "name": "label",
5 "value": "Bus. Part. ID",
6 "namespace": "http://www.sap.com/Protocols/SAPData"
7 }],
8 "sap:label": "Bus. Part. ID"
9 }
OData V4 어노테이션
엔티티 모델의 각각 요소는 어노테이션을 달 수 있다. 기존 sap.ui.model.odata.ODataAnnotations 로 부터의 어노테이션은 해당 요소에 직접적으로 병합된다.
아래 코드는 위에서 설명한 것처럼 기존 sap.ui.model.odata.ODataMetadata로부터의 구조를 보여준다.
"dataServices" : {
"schema" : [{
"namespace" : "GWSAMPLE_BASIC",
"entityType" : [{
"name" : "Product",
"property" : [{
"name" : "ProductID",
"type" : "Edm.String",
"nullable" : "false",
"maxLength" : "10"
}, {
"name" : "SupplierName",
"type" : "Edm.String",
"maxLength" : "80",
"extensions" : [{
"name" : "label",
"value" : "Company Name",
"namespace" : "http://www.sap.com/Protocols/SAPData"
}, {
"name" : "creatable",
"value" : "false",
"namespace" : "http://www.sap.com/Protocols/SAPData"
}, {
"name" : "updatable",
"value" : "false",
"namespace" : "http://www.sap.com/Protocols/SAPData"
}],
"sap:label" : "Company Name",
"sap:creatable" : "false",
"sap:updatable" : "false"
"Org.OData.Core.V1.Computed" : {
"Bool" : "true"
}
}, {
"name" : "WeightMeasure",
"type" : "Edm.Decimal",
"precision" : "13",
"scale" : "3",
"Org.OData.Measures.V1.Unit" : {
"Path" : "WeightUnit"
}
}, {
"name" : "WeightUnit",
"type" : "Edm.String",
"maxLength" : "3"
}],
"com.sap.vocabularies.UI.v1.DataPoint" : {
"Value" : {
"Path" : "WeightMeasure",
"EdmType" : "Edm.Decimal"
}
},
"com.sap.vocabularies.UI.v1.Identification" : [{
"Value" : {"Path" : "ProductID"}
}, {
"Value" : {"Path" : "SupplierName"}
}, {
"Value" : {"Path" : "WeightMeasure"}
}]
}]
}]
}
OData Meta Model의 향상
각 sap:label 과 같이 기본 용어에 해당하는 SAP-specific OData 어노테이션에 쉽게 접근하는데다가, 어노테이션은 기존 sap.ui.model.odata.ODataAnnotations의 OData 버전 4.0 어노테이션 안에서 정의 되지 않았을 때에서만 믹스된다.
OData V2 내 통화 및 단위 정의
양이나 치수 는 CLDR에 정의된 것 보다 다른 통화, 또는 단위가 필요할 수 있다.
The sap.ui.model.odata.type.Currency 그리고 sap.ui.model.odata.type.Unit 데이터 타입들은 통화 코드 그리고 단위를 커스터마이징 할 때 코드목록을 사용할 수 있다.
통화 또는 유닛 커스터마이징 하는 코드 목록의 경우 어노테이션들 정의해야한다.
OData V4 시나리오와 대조적으로 OData V2는 코드 리스트 서비스를 허용하지 않는다. 모든 메터데이터 정보는 기본 metadata.xml 파일에 포함되어야 하며, 코드리스트 URL은 단지 이 파일만을 가리켜야 한다. 이것은 다음과 같이 URL 속성을 명시하여 수행된다.
<PropertyValue Property="Url" String="./$metadata"/>
com.sap.vocabularies.CodeList.v1.CurrencyCodes 또는 com.sap.vocabularies.CodeList.v1.UnitsOfMeasure 어노테이션이 참조된 코드리스트는 이것들이 필요하다.
유일한 키 속성인 내부 코드
의존적 언어 설명
특정 단위 숫자 속성
옵션 : 내부 코드 대신 보여지는 외부 코드
옵션 : 표준 코드
키 속성 문서화:
설명 속성을 가리키는 com.sap.vocabularies.Common.v1.Text
숫자 속성을 가리키는 com.sap.vocabularies.Common.v1.UnitSpecificScale
표준 코드를 가리키는 com.sap.vocabularies.CodeList.v1.StandardCode
어노테이션 안의 경로 속성은 엔티티 타입 안 속성들을 참조해야하는 것을 명심해야한다. 네비게이션 속성을 포함하는 경로는 지원되지 않는다.
대체키가 가능할 때 타입은 통화 또는 단위의 키로서 대신 사용한다. 이 경우엔, 그 서비스는 통화 또는 단위 속성안에서 대체 키 묘사를 포함해야 한다. 대체키가 주석이 달리지 않았을 땐, 키는 데이터 안에서 사용, 예상된다. 최대 하나의 키를 가져야 하며, 키와 대체키는 정확히 하나여야 한다.
com.sap.vocabularies.CodeList.v1.StandardCode 로서 문서화된 속성은 sap.ui.model.odata.type.Currency 에서 ISO 코드로 해석된다. 그리고 통화 상징을 찾는데 사용된다. 이 통화 상징은 데이터 입력에 사용된다.
service’s metadata.xml 파일 내 통화 코드와 단위 코드 리스트 문서
...
<edmx:Include Namespace="com.sap.vocabularies.Common.v1" Alias="SAP__common"/>
<edmx:Include Namespace="Org.OData.Core.V1" Alias="SAP__core"/>
<edmx:Include Namespace="com.sap.vocabularies.CodeList.v1" Alias="SAP__CodeList"/>
...
<EntityType Name="Product">
...
<Property Name="WeightMeasure" Type="Edm.Decimal" Precision="13" Scale="3" />
<Property Name="WeightUnit" Type="Edm.String" MaxLength="3" />
<Property Name="CurrencyCode" Type="Edm.String" Nullable="false" MaxLength="5" />
<Property Name="Price" Type="Edm.Decimal" Precision="16" Scale="3" />
...
</EntityType>
...
<EntityType Name="SAP__Currency" sap:content-version="1">
<Key>
<PropertyRef Name="CurrencyCode"/>
</Key>
<Property Name="CurrencyCode" Type="Edm.String" Nullable="false" MaxLength="5" sap:label="Currency" sap:semantics="currency-code"/>
<Property Name="ISOCode" Type="Edm.String" Nullable="false" MaxLength="3" sap:label="ISO Code"/>
<Property Name="Text" Type="Edm.String" Nullable="false" MaxLength="15" sap:label="Short Text"/>
<Property Name="DecimalPlaces" Type="Edm.Byte" Nullable="false" sap:label="Decimals"/>
</EntityType>
<EntityType Name="SAP__UnitOfMeasure" sap:content-version="1">
<Key>
<PropertyRef Name="UnitCode"/>
</Key>
<Property Name="UnitCode" Type="Edm.String" Nullable="false" MaxLength="3" sap:label="Internal UoM" sap:semantics="unit-of-measure"/>
<Property Name="ISOCode" Type="Edm.String" Nullable="false" MaxLength="3" sap:label="ISO Code"/>
<Property Name="ExternalCode" Type="Edm.String" Nullable="false" MaxLength="3" sap:label="Commercial"/>
<Property Name="Text" Type="Edm.String" Nullable="false" MaxLength="30" sap:label="UoM Text"/>
<Property Name="DecimalPlaces" Type="Edm.Int16" sap:label="Decimal Places"/>
</EntityType>
...
<EntityContainer Name="GWSAMPLE_BASIC_Entities" m:IsDefaultEntityContainer="true" sap:message-scope-supported="true" sap:supported-formats="atom json xlsx">
<EntitySet Name="ProductSet" EntityType="GWSAMPLE_BASIC.Product" sap:content-version="1"/>
...
<EntitySet Name="SAP__Currencies" EntityType="GWSAMPLE_BASIC.SAP__Currency" sap:creatable="false" sap:updatable="false" sap:deletable="false" sap:pageable="false" sap:content-version="1"/>
<EntitySet Name="SAP__UnitsOfMeasure" EntityType="GWSAMPLE_BASIC.SAP__UnitOfMeasure" sap:creatable="false" sap:updatable="false" sap:deletable="false" sap:pageable="false" sap:content-version="1"/>
...
<Annotations
xmlns="http://docs.oasis-open.org/odata/ns/edm"
Target="GWSAMPLE_BASIC.GWSAMPLE_BASIC_Entities">
<Annotation Term="SAP__CodeList.CurrencyCodes">
<Record>
<PropertyValue Property="Url" String="./$metadata"/>
<PropertyValue Property="CollectionPath" String="SAP__Currencies"/>
</Record>
</Annotation>
<Annotation Term="SAP__CodeList.UnitsOfMeasure">
<Record>
<PropertyValue Property="Url" String="./$metadata"/>
<PropertyValue Property="CollectionPath" String="SAP__UnitsOfMeasure"/>
</Record>
</Annotation>
</Annotations>
<Annotations Target="SAP__self.Currency/CurrencyCode">
<Annotation Term="Common.Text" Path="Text" />
<Annotation Term="Common.UnitSpecificScale" Path="DecimalPlaces" />
<Annotation Term="CodeList.StandardCode" Path="ISOCode" />
</Annotations>
<Annotations
xmlns="http://docs.oasis-open.org/odata/ns/edm"
Target="GWSAMPLE_BASIC.SAP__UnitOfMeasure/UnitCode">
<Annotation Term="Common.Text" Path="Text" />
<Annotation Term="Common.UnitSpecificScale" Path="DecimalPlaces" />
<Annotation Term="CodeList.StandardCode" PropertyPath="ISOCode" />
<Annotation Term="CodeList.ExternalCode" PropertyPath="ExternalCode" />
</Annotations>
<Annotations
xmlns="http://docs.oasis-open.org/odata/ns/edm"
Target="GWSAMPLE_BASIC.SAP__UnitOfMeasure">
<Annotation Term="Core.AlternateKeys">
<Collection>
<Record>
<PropertyValue Property="Key">
<Collection>
<Record>
<PropertyValue Property="Name" PropertyPath="ExternalCode" />
<PropertyValue Property="Alias" String="ExternalCode" />
<Record>
</Collection>
</PropertyValue>
<Record>
</Collection>
</Annotation>
</Annotations>
...
위 메타데이터를 통해 sap.ui.model.odata.type.Currency 와 sap.ui.model.odata.type.Unit 같은 입력 필드의 데이터 타입을 사용할 수 있다. 데이터 타입들은 금액 또는 측정을 첫번째로, 통화코드 또는 단위를 두번째로, 그리고 커스텀한 코드리스트 정보를 세번째로 하여 정보를 합쳐 사용한다.
...
<Input value="{
mode:'TwoWay',
parts:[
'WeightMeasure',
'WeightUnit',
{
mode:'OneTime',
path:'/##@@requestUnitsOfMeasure',
targetType:'any'}],
type:'sap.ui.model.odata.type.Unit'}"/>
...
<Input value="{
mode:'TwoWay',
parts:[
'Price',
'CurrencyCode',
{
mode:'OneTime',
path:'/##@@requestCurrencyCodes',
targetType:'any'}],
type:'sap.ui.model.odata.type.Currency'}"/>
...
코드리스트는 브라우저 세션 및 코드목록 URL당 한번만 자동으로 요청된다.
-
-
자바의 인터페이스
목적
자바의 인터페이스에 대해 학습하세요.
학습할 것
인터페이스 정의하는 방법
인터페이스 구현하는 방법
인터페이스 레퍼런스를 통해 구현체를 사용하는 방법
인터페이스 상속
인터페이스의 기본 메소드 (Default Method), 자바 8
인터페이스의 static 메소드, 자바 8
인터페이스의 private 메소드, 자바 9
인터페이스란
인터페이스는 일종의 추상 클래스로 추상클래스보다 추상화 정도가 높아 일반 메서드 또는 멤버변수 (몸통)을 가질 수 없다 오직 추상메서드와 상수만 멤버로 가질 수 있다. 비유하자면 밑그림만 그려져있는 기본 설계도 라 할수있다.
1. 인터페이스 정의하는 방법
interface 인터페이스 이름{
public static final 타입상수이름 = 값;
public abstract 메서드이름(매개변수 목록);
}
인터페이스는 클래스랑 달리 제약사항이 있다.
모든 멤버변수는 public static final 이어야 하며, 이를 생략할 수 있다.
모든 메서드는 public abstract 이어야 하며 생략할 수 있다. (static 메서드와 default 메서드 예외)
위의 static 메서드와 default 메서드는 JDK 1.8에서 지원한다.
2. 인터페이스 구현하는 방법
인터페이스는 그 자체로 생성할 수 없고, 자신의 몸통을 만들어줘야 한다.
public interface Player{
public void move(int x, int y);
public void shoot(int x, int y);
}
class NBAPlayer implements Player{
public void move(int x, int y){
System.out.println("x, y 좌표로 이동");
}
public void shoot(int x, int y){
System.out.println("x, y 방향으로 슛!");
}
}
인터페이스의 메서드 중 일부만 구현하고 싶다면 abstract 붙여서 추상클래스로 선언해야 한다.
abstract class RunningPlayer implements Player{
public void move(int x, int y);
}
3. 인터페이스 레퍼런스를 통해 구현체를 사용하는 방법
문제의 뜻을 잘 이해하지 못했는데 일단 내가 해석한 것은 main에서 인터페이스 같은경우 어떻게 선언하는 것인지 같아 적어본다.
public static void main(String[] args){
Player Lebron = new NBAPlayer();
lebron.move();
lebron.shoot();
}
x, y 좌표로 이동
x, y 방향으로 슛!
NBAPlayer()라는 클래스는 Player라는 인터페이스를 참조하고 있으므로 인터페이스 변수 선언에 참조된 클래스를 생성하여 사용할 수 있다.(?)
4. 인터페이스 상속
인터페이스는 인터페이스로부터만 상속받을 수 있으며 클래스와는 달리 다중상속, 여러개의 인터페이스로부터 상속받는 것은 불가능하다.
interface Moving{
void move(int x, int y);
}
interface Shooting{
void shoot(int x, int y);
}
interface Player extends Moving, Shooting{}
클래스의 상속과 마찬가지로 자손 인터페이스는 조상인터페이스에 정의된 멤버를 모두 상속받아 Player 인터페이스는move와 shoot을 다 가진 인터페이스가 된다.
5. 인터페이스의 기본 메소드(Default Method) 자바 8
조상클래스에 메서드 추가는 쉽지만 인터페이스는 어렵다. 인터페이스에 메서드 추가는 그 인터페이스를 참조하는 모든 클래스들이 새로 추가된 메서드를 구현해야 하기 때문이다. 그래서 디폴트 메서드가 나왔으며 이는 추상 메서드의 기본적 구현을 제공하는 메서드로 참조중인 클래스를 변경하지 않아도 된다.
interface MyInterface{
void method();
default void newMethod();
}
대신 이때 기존 메서드와 디폴트 메서드가 이름이 중복되어 충돌할 수도 있다.
여러 인터페이스의 디폴트 메서드 간의 충돌
인터페이스를 구현한 클래스에서 디폴트 메서드를 오버라이딩해야 한다.
디폴트 메서드와 조상 클래스의 메서드 간의 충돌
조상 클래스의 메서드가 상속되고, 디폴트 메서드는 무시된다.
라는 규칙이 있는데 간단히 필요한 쪽의 메서드와 같은 내용으로 오버라이딩 해버리면 된다.
6. 인터페이스의 static 메소드, 자바8
static 메소드 같은 경우는 인스턴스와 관계가 없는 독립적 메서드로 인터페이스에 추가하면 된다.
그리고 인터페이스의 static메서드 역시 접근 제어자가 항상 public이며 생략할 수 있다.
7. 인터페이스의 private 메소드, 자바 9
java 8에서는 default, static method는 기본으로 public 만 가능했고 java 9 버전부턴 private 메서드를 지원한다.
이는 인터페이스가 내부적으로 처리하는 문제를 외부에서 사용하지 못하게 만들어준다.
결과적으로 인터페이스에 캡슐화시켜준다 할 수 있다.
Reference
남궁성. Java의 정석
-
자바의 패키지
목적
자바의 패키지에 대해 학습하세요.
학습할 것
package 키워드
import 키워드
클래스패스
CLASSPATH 환경변수
-classpath 옵션
접근지시자
1. package 키워드
패키지
패키지는 클래스랑 인터페이스의 모음이며 관련된 클래스끼리 그룹단위로 모아 효율적인 관리를 할 수 있다.
패키지가 다르면 클래스명이 같아도 구분할 수 있다.
클래스가 물리적으로 하나의 클래스파일(.class).인 것과 같이 패키지는 물리적으로 하나의 디렉토리이다.
하나의 소스파일에 첫 번째 문장으로 단 한 번의 패키지 선언만을 허용한다.
모든 클래스는 반드시 하나의 패키지에 속해야 한다.
패키지는 점(.)을 구분자로 하여 계층구조를 구성할 수 있다.
패키지는 물리적으로 클래스 파일(.class)을 포함하는 하나의 디렉토리이다.
패키지 선언
패키지 선언은 아래 한 줄을 맨 첫번째 줄에 적어주면 된다.
package 패키지명;
2. import키워드
import문
다른 패키지의 클래스를 사용하려 한다면 패키지명이 포함된 클래스 이름을 사용해야 한다. import의 역할은 컴파일러에게 소스파일에 사용된 클래스의 패키지에 대한 정보를 제공하는 것이다. 이클립스에서 Ctrl + Shift + o 누르면 자동으로 import 해준다.(근데 자동완성에 익숙해져서 잘 안쓸거 같다.)
import문 선언
import 패키지명.클래스명; //한 클래스만 사용할 때
import 패키지명.*; // 패키지 안에 여러클래스를 사용할 때
패키지명.*; 을 사용한다 하면 그안의 모든 클래스들을 로드해 필요없는 클래스도 import 할거라 생각하지만, 실행시 성능에 이상이 없다.
static import문
static import는 static 멤버를 호출할 때 클래스 이름을 생략하게 해준다. 예로
import static java.lang.Math.random;
선언 시,
Math.random(); -> random();
처럼 바꿔준다.
3. 클래스패스
간단히 말해서 클래스를 찾기 위한 경로이다.
우리는 프로그램을 실행하면 메모리를 할당받고 JVM을 동작시킨다.
컴파일러를 실행하게 되면 JVM이 동작하게 된다. JVM은 .java 파일들을 클래스 파일들을 찾는데 그때의 파일 경로의 기준이 된다. 그리고 이를 지정해주는 방법은 classpath 환경변수, -classpath 옵션 두가지가 있다.
4. CLASSPATH 환경변수
JVM 클래스 로더는 classpath 경로를 찾아가 그 안에 정의된 class들을 가져온다.
이 것을 지정해주는 법은 os의 시스템 변수 부분 Path의 값에 %JAVA_HOME%/bin 이라는 값을 추가해주면 등록된다.
5. -classpath 옵션
자바에서 컴파일 할 때 classpath를 정해서 갈 수 있다.
java c -classpath '경로' java파일명
이라는 명령어를 통해서 classpath의 옵션을 지정한다.즉, 다른 클래스에 의존하는 클래스 파일을 컴파일 하기 위해 그 클래스의 경로를 직접 매핑해준다.
6. 접근지시자 (접근제어자)
멤버 또는 클래스에 사용되며, 해당하는 멤버 또는 클래스를 외부에서 접근하지 못하도록 제한하는 역할을 한다.
접근제어자가 사용될 수 있는 곳 - 클래스, 멤버변수, 메서드 생성자
제어자
같은 클래스
같은 패키지
자손클래스
전 체
public
O
O
O
O
protected
O
O
O
(default)
O
O
private
O
public : 접근제한이 전혀 없다.
private : 같은 클래스 내에서만 사용하도록 제한
default : 같은 패키지 내의 클래스에서만 접근 가능
protected : 패키지에 관계없이 상속관계에 있는 자손클래스에서 접근할 수 있도록 하는 것이 제한목적이지만, 같은 패키지내에서도 접근이 가능하다. 결과적으로 protected가 default보다 접근범위가 더 넓다.
접근 제어자를 이용한 캡슐화
접근제어자를 왜 사용하냐 : 클래스 내부에 선언된 데이터를 보호하기 위해서이다. 데이터가 유효한 값을 유지하도록, 비밀번호와 같은 데이터를 외부에서 함부로 변경하지 못하도록 하기 위해서이며 이것을 데이터 감추기라 부르기도 한다.
외부에는 불필요한, 내부적으로만 사용되는 부분을 감추기 위해서도 사용된다.
예를들어
public class Time{
public int hour;
public int minute;
public int second;
}
클래스가 있고 인스턴스 생성 후 멤버변수에 직접 접근하면 값을 변경할 수 있다.
Time t = new Time();
t.hour = 25;
위 코드처럼 hour에 25라는 잘못된 값을 지정한다 해도 막을 방법이 없다.
이럴 때는 private나 protected로 제한하고 값을 읽고 변경할 수 있는 public 메서드를 제공함으로써 간접적으로 멤버변수의 값을 다룰 수 있도록 한다.
public void set Hour(int hour){
if (hour < 0 || hour > 23){
return ;
}
this.hour = hour;
}
만일 상속을 통해 확장될 것이 예상되는 클래스라면, 멤버에 접근 제한을 주되 자손 클래스에서 접근하는 것이 가능하도록 하기 위해 private 대신 protected를 사용한다.
생성자의 접근 제어자
생성자에 접근 제어자를 사용함으로 인스턴스 생성을 제한할 수도 있다.
생성자의 접근 제어자를 private 로 지정하면 외부에서 인스턴스를 생성할 수 없게 된다.
class SingleTon{
private SingleTon() {
}
}
대신 인스턴스를 생성해서 반환해주는 public 메서드를 제공해 외부에서 이 클래스의 인스턴스를 사용할 수 있도록 할 수 있다. 이 메서드는 public 인 동시에 static 이어야 한다.
class Singleton(){
private static Singleton s = new Singleton();
private Singleton(){
}
public static Singleton getInstance(){
return s;
}
}
또 생성자가 private인 클래스는 다른 클래스의 조상이 될 수 없다. 자손클래스가 인스턴스를 생성할 때 조상클래스의 생성자를 호출해야 하는데 private는 같은 클래스 내에서만 가능하기 때문이다. 따라서 클래스 앞에 final을 더 추가해 상속할 수 없는 클래스인 것을 알려주는 것이 좋다.
제어자의 조합
대 상
사용 가능한 제어자
클래스
public, (default), final, abstract
메서드
모든 접근 제어자, final, abstract, static
멤버변수
모든 접근 제어자, final, static
지역변수
final
메서드에 static과 abstract를 함께 사용할 수 없다.
static 메서드는 구현된 메서드에서만 사용할 수 있기 때문에
클래스에 abstract와 final을 동시에 사용할 수 없다.
클래스에서 사용되는 final은 확장을 없애는 의미이고 abstract는 상속을 통해서 완성되기에 모순된다.
abstract메서드의 접근 제어자가 private일 수 없다.
abstract 메서드는 자손 클래스를 구현해주어야 하는데 접근 제어자가 private 이면 자손에 접근할 수가 없다.
메서드에 private가 final을 같이 사용할 수 없다.
접근 제어자가 private인 메서드는 오버라이딩 될 수 없다.
Reference
남궁성. Java의 정석
-
자바의 상속
목적
자바의 상속에 대해 학습하세요.
학습할 것
자바 상속의 특징
super 키워드
메소드 오버라이딩
다이나믹 메소드 디스패치 (Dynamic Method Dispatch)
추상 클래스
final 키워드
Object 클래스
1. 자바 상속의 특징
자바의 상속 전, 상속이란, 기존 클래스를 재사용하여 새로운 클래스를 작성하는 것이다. 이는 코드의 재사용성을 높이고 중복을 제거하여 생산성과 유지성에 크게 기여한다.
단일 상속
다른 객체지향 언어인 C++ 에서는 여러 조상 클래스로부터 상속받는 것이 가능한 ‘다중상속’을 허용하지만, 자바는 오직 단일 상속만 허용한다. 만약 다중 상속을 허용하게 된다면 클래스 내 인스턴스 메서드를 구별할 방법이 없다. 이처럼 명확하고 신뢰성 있는 코드를 위해 다중 상속의 장점을 포기하며 단일 상속을 지원한다.
상속 횟수 제한 X
자바에서는 상속에 제한을 두지 않는다.
최상의 클래스
자바에서는 모든 상속계층도의 최상위에 Object 클래스가 위치한다. 따라서 Object 클래스에 정의된 멤버들을 사용할 수 있다.
2. super 키워드
super란 자손 클래스에서 조상 클래스로부터 상속받은 멤버를 참조하는데 사용되는 참조변수이다.
멤버변수와 지역변수의 이름이 같을 때 this를 붙여서 구별했듯이, 상속받은 멤버와 자신의 멤버가 같을 떈 super를 붙여서 구별한다.
조상 클래스 멤버와 자손 클래스의 멤버가 중복 정의되어 서로 구별해야 하는 경우에만 super를 사용하는 것이 좋다.
super는 static 메서드에선 사용할 수 없다.
조상 클래스에서 선언된 멤버변수와 같은 이름의 멤버변수를 가진 자손 클래스엔 중복되게 정의가 가능하며 super를 사용해 구별해준다.
메서드 역시 super를 사용하여 호출할 수 도있다.
ex) super.getName();
생성자에서는 super()는 생성자이고 조상 클래스의 생성자를 호출하는데 사용된다. 그리고 자손 클래스의 멤버가 조상 클래스의 멤버를 사용할 수 있으므로 조상의 멤버가 먼저 초기화 되어야 한다. (super() 가 가장 먼저 선언되어야 하는 이유) 혹시나 적어놓지 않았으면 컴파일러가 자동으로 추가해준다.
만약 조상 클래스의 값들을 가지고 생성자를 호출하고 싶다면 super(변수, 변수 …) 이런 식으로 호출하면 된다.
3. 메소드 오버라이딩
오버라이딩이란 상속받은 메서드의 내용을 변경하여 사용하는 것이다. 자손 클래스에 맞게 변경해야 하는 경우가 많고 이럴 때 오버라이딩을 사용한다.
오버라이딩의 조건을 성립시키기 위해선 3가지 조건이 맞아야 한다.
이름이 같아야 한다.
매개변수가 같아야 한다.
반환타입이 같아야 한다.
요약하면 선언부가 일치해야 한다고 말할 수 있고 접근 제어자와 예외같은 경우는 제한된 조건이 있다.
접근 제어자는 조상 클래스 메서드보다 좁은 범위로 변경할 수 없다.
예외는 조상 클래스의 메서드보다 많이 선언할 수 없다.
인스턴스메서드와 static 메서드를 서로 변경해서 사용할 수 없다.
(static 같은 경우는 오버라이딩이 아니라 그냥 static 메서드를 정의 한 것이다. )
4. 다이나믹 메소드 디스패치
다이나믹 메소드 디스패치에 대한 정보가 안나와서 나름 다른 분들의 글과 인터넷 검색을 통해서 찾아 보았고 정확한 정보를 알 순 없었다.
허나 일단 글대로 적자면,
다이나믹 메소드 디스패치는 오버라이딩 되어있는 메소드를 실행할 때 런타임 시점에서 어떤 메소드를 실행할 지 결정하려는 것이다.
인터페이스를 참조한 2개의 자식 클래스가 있고, 이에 인터페이스를 생성하여 그 안의 함수를 실행시켰을 때, 과연 2개의 자식 클래스 중 누가 호출되는 것인 지를 컴파일러가 알 수 없는 것이다.
5. 추상 클래스
추상 클래스는 클래스에 정의할 내용을 추상적으로 적어놓은, 즉 설계도의 틀 이라고 생각하면 좋다. 추상클래스는 상속을 통해서 자손 클래스에 의해서만 완성될 수 있다. 추상 클래스는 클래스 앞에 abstract를 붙이면 된다. 긜고 상속을 통해 구현해주면 완성된다. 또한 추상클래스에서도 생성자가 있으며, 멤버변수와 메서드도 가질 수 있다.
abstract class 클래스이름 {
}
스포츠를 예시로 들자면 축구 농구 배구 등이 있고 축구 선수 배구 선수 농구 선수가 있는데 이 3명의 선수는 스포츠 선수라는 타이틀을 가지고 있고 추상화를 통해 스포츠 선수를 정의할 수 있다.
abstract class Player{
boolean pause;
int currentPos;
Player(){
pause = false;
currentPos = 0;
}
abstract void play();
abstract void stop();
}
6. final 키워드
변경될 수 없는 의미를 가지고 있는 의미이며 선언시 변경할 수 없는 상수가 된다.
메서드에 사용 시엔 오버라이딩을 할 수 없고 클래스에 사용하면 자손 클래스를 정의하지 못하게 된다.
final 사용 구간은 클래스, 메서드, 멤버변수, 지역변수 이다.
final이 붙은 변수는 상수이므로 선언과 초기화를 동시에 하지만, 인스턴스 변수의 경우 생성자에서 초기화 되도록 할 수 있다. 클래스 내에 매개변수를 갖는 생성자를 선언하여, 인스턴스를 생성할 때 final이 붙은 멤버변수를 초기화하는데 필요한 값을 생성자의 매개변수로부터 제공받는 것이다. 이를 통해 각 인스턴스마다 final이 붙은 멤버변수가 다른 값을 갖도록 하는 것이 가능하다. 이것이 불가능 하다면 final이 붙은 인스턴스 변수는 모든 인스턴스에서 같은 값이 되어야만 한다.
7. Object 클래스
Obejct 클래스는 모든 클래스의 최상위 위치에 있는 조상 클래스이다.
class Study extends Object{
}
모든 클래스들은 Object 클래스를 상속받고 있다. 이를 통해 모든 클래스는 Object 클래스에 정의된 멤버, 함수들을 사용할 수 있다.
Object 클래스의 메서드
설 명
protected Object clone()
객체 자신의 복사본을 반환한다.
public boolean equals(Object obj)
객체 자신과 객체 obj가 같은 객체인지 알려준다.(같으면 true)
protected void finalize()
객체가 소멸될 때 가비지 컬렉터에 의해 자동적으로 호출된다. 이 때 수행되어야하는 코드가 있을 때 오버라이딩 한다.(거의 사용 안함)
public Class getClass()
객체 자신의 클래스 정보를 담고 있는 Class인스턴스를 반환한다.
public int hashCode()
객체 자신의 해시코드를 반환
public String toString()
객체 자신의 정보를 문자열로 반환
public void notify()
객체 자신을 사용하려고 기다리는 쓰레드를 하나만 깨운다.
public void notifyAll()
객체 자신을 사용하려고 기다리는 모든 쓰레드를 깨운다.
public void wait() public void wait(long timeout) public void wait(long time, int nanos)
다른 쓰레드가 notify()나 notifyAll()을 호출할 떄까지 현재 쓰레드를 무한히 또는 지정된 시간(timeout, nanos)동안 기다리게 한다.
-
자바의 Class
목적
자바의 Class에 대해 학습하세요.
학습할 것
클래스 정의하는 방법
객체 만드는 방법 (new 키워드 이해하기)
메소드 정의하는 방법
생성자 정의하는 방법
this 키워드 이해하기
1. 클래스 정의하는 방법
클래스란, 객체를 정의해놓은 것, 또는 객체의 설계도 라고 정의할 수 있다. 그리고 클래스는 객체를 생성하는데 사용된다.
클래스를 구성하는 요소들은 필드들과 메소드, 생성자로 되어있다.
public class Account{
private Long id;
private String name;
private String email;
private String password;
/* 생성자는 default 하게 만들 수 있지만 생성자에서 파라미터로 값을 받아서 작성 시, 반드 시 작성해주어야 한다.
*/
public Account(String email, String password){
this.email = email;
this.password = password;
}
public String getEmail(){
return Email;
}
public void changePassword(String password){
this.password = password;
}
}
2. 객체 만드는 방법(new 키워드 이해하기)
클래스로부터 객체를 만드는 과정을 인스턴스화 라고 하며 만들어진 객체를 그 클래스의 인스턴스라고 한다. 그리고 객체는 속성과 기능 두 종류의 구성요소로 이루어져 있으며 이들은 그 객체의 멤버라고 한다. 속성과 기능 같은 경우, 속성은 멤버변수, 기능은 메소드 라고 말한다.
클래스명 변수명; // 선언
변수명 = new 클래스명(); // 생성
기본적으로 인스턴스 생성( 객체 만들기 )는 위 코드처럼 정의하며 위 클래스를 예시로 들었을 땐,
Account account;
account = new Account(); // 기본 생성자
account = new Account("dmstjd1024@naver.com", "password") // 파라미터 있는 생성자
이런 식으로 구성된다.
3. 메소드 정의하는 법
반환타입 메서드이름 (타입 변수명, 타입변수명, ... )
{
//메서드 호출 시 수행될 코드
}
ex)
int add (int a, int b)
{
int result = a + b;
return result;
}
4. 생성자 정의하는 방법
컴파일 시 소스파일에 생성자가 하나도 정의되지 않은 경우, 컴파일러는 자동적으로 아래와 같은 내용의 기본 생성자를 추가하여 컴파일 한다.
클래스이름() {}
조심해야 할 점은 클래스 내에 생성자가 하나도 없을 때 이므로 혹시나 뜬금없이 파라미터를 가진 생성자만 적어놓으면 에러가 난다.
클래스 클래스명{
변수
변수
생성자(파라미터 파라미터명){
// 에러 발생
}
}
5. this 키워드 이해하기
생성자 간에도 서로 호출이 가능한다. 허나 2가지 조건이 필요한다.
생성자의 이름으로 클래스이름 대신 this를 사용
한 생성자에서 다른 생성자를 호출할 때는 반드시 첫 줄에서만 호출이 가능
ex)
Car (String color){
door = 5;
Car(color, "auto", 4); // X 생성자에서 다른 생성자를 호출 => this(color ....) 생성 O
}
첫줄에서만 호출 가능한 이유는 생성자 내에서 ㅗ기화 작업 도중, 다른 생성자를 호출하게 되면, 호출된 다른 생성자 내에서도 멤버변수들의 값을 초기화를 할 것이므로 다른 생성자를 호출하기 이전의 초기화 작업이 무의미해질수 있기 떄문이다.
그리고 this같은 경우는
Car(){
this("white", "auto", 4); // 처럼 깔끔하게 적는 것이 보기 더 좋다.
}
또한 this를 이용해서 인스턴스변수에 접근할 수 있다. 허나 this는 인스턴스 변수에만 접근할 수 있다. static 메서드에서는 인스턴스를 사용할 수 없는 것처럼, this 역시 사용할 수 없다. static 메서드는 인스턴스를 생성하지 않고도 호출될 수 있기 때문이다.
정리하자면
this 인스턴스 자신을 가리키는 참조변수, 인스턴스의 주소가 저장되어 있따. 모든 인스턴스메서드에 지역변수로 숨겨진 채로 존재한다.
this(), this(매개변수) 생성자, 같은 클래스의 다른 생성자를 호출할 때 사용한다.
Reference
남궁성. Java의 정석
-
제어문
목적
자바가 제공하는 제어문을 학습하세요
학습할 것
선택문
반복문
1. 선택문
Switch
if 같은 조건문은 참, 거짓밖에 없기 때문에 경우가 많아질 수록 else if를 계속 추가해야하며 else if 에 있는 조건식들을 일일히 다 계산하며 내려가야 하므로 처리 시간이 늘어난다. 이를 해결하기 위해 switch(선택)문이 있고 하나의 조건식으로 많은 수를 처리할 수 있으며 표현도 간결해 가독성이 높다.
switch문은 조건식을 먼저 계산한 다음, 결과랑 일치하는 case 문으로 이동한다. 그리고 case 문의 내용을 수행하며 break를 만났을 때 switch문을 빠져나오게 된다.
또한 조건식의 결과와 일치하는 case 문이 없는 경우, default 로 지정한 문을 찾아가 내용을 수행한다. 그렇기 때문에 default를 기마지막에 놓는다. (이렇게 하면 break문이 필요 없음)
책에서는 break문을 사용해 권한 주는 코드로도 활용할 수 있다 한다. (ex )
swich (level){
case 3 :
grantDelete();
case 2 :
grantWrite();
case 1 :
grantRead();
break;
}
간단히 말해서 case에 break를 주지 않음으로 아래 권한 부여 함수들을 다 실행시킬 수 있다는 것이다.
switch 제약조건
조건식의 결과값은 반드시 정수 또는 문자열여야 한다.
중복되지 않아야 한다.
case문의 값은 반드시 상수이어야 한다.
※ tip switch 안에 switch로 중첩이 가능한다.-> break문 으로 탈출 조심!
2. 반복문
반복문은 반복적으로 수행될 때 사용되며, for, while, do - while이 있다.
1. for
for문은 반복 횟수를 알고 있을 때 정확한다. 구조가 복잡하지만, 직관적이라 오히려 이해하기 쉽다.
for문의 구조와 수행순서
for(초기화; 조건식; 증감식){
수행문장
}
① 초기화 -> ② 조건식 -> ③ 증감식 -> ④ 수행될 문장 순서대로 진행한다.
초기화
변수 초기화 하는 부분이며 처음 한번만 수행한다. ‘,’ 를 통해서 두 개 이상의 변수를 초기화 가능하다.(단 변수 타입은 같아야 한다.)
조건식
참이면 반복하고 거짓이면 반복 멈춘다. (무한루프에 빠지지않게 조건식을 잘 세워야 한다.)
증감식
반복문 제어 변수를 증감시키는 식이다. ex) i++, i==, i+=2, i* = 3
쉼표를 이용해서 두 문장 이상을 하나로 연결해 쓸 수 있다. ex) for( ~~; ~~; i++, j– )
for문에서는 for(;;){} 을 사용하면 계속 참으로 간주되어 무한 루프로 돌릴 수 있다. (break로 탈출)
항상된 for문
JDK 1.5부터 배열과 컬렉션에 저장된 요소에 접근할 때 편리한 방법의 문법이 추가되었다.
for( 타입 변수명, 배열 또는 컬렉션) {
수행문장
}
위 for문을 사용할 때는 배열, 컬렉션 만 가능하고 안에 저장된 값들을 하나씩 읽어와 변수에 저장해 반복문이 돌아간다.
예시
for (int i=0; i < arr.length; i++) -> for(int tmp : arr)
2. while
while은 for문과 다르게 구조가 간단하며 참인동안 반복될 문장들을 적어놓는다.
while(조건식){
반복될 문장
}
for문과 while문 비교
for문은 초기화, 조건식, 증감식을 한 곳에 모아놓은 것일 뿐 while문과 다르지 않아 항상 서로 형변환이 가능하다. 초기화나 증감식이 필요하지 않는 경우면 while문이 더 적합하다.
while문의 조건식은 생략 불가
때문에 while문의 조건식이 항상 참이 되도록 하려면 안에 true를 넣어주면 된다.
3. do - while
while문의 변형으로 조건식과 불럭의 순서를 바꿔놓은 것이다. 따라서 블럭이 먼저 수행한 후에 조건식을 평가한다.
do {
수행될 문장
}while(조건식);
따라서 최소한 한번은 수행될 것을 보장한다.
4. 부가적
1.break 문
break문은 switch에서 설명 했는데 일단은 실질적인 의미로 break문은 자신이 포함된 가장 가까운 반복문을 벗어나는 문이다. 주로 if문과 함께 사용되어 특정 조건을 만족하면 반복문을 벗어나도록 한다.
2.continue 문
반복문 내에서만 사용될 수 있으며, 반복 중 continue를 만나게 되면 가장 가까운 반복문의 끝으로 이동하여 다음 반복문으로 넘어간다.
3.이름 붙은 반복문
break와 continue 같이 가장 가까운 반복문 외에 중첩되어 있는 반복문들 중에서 지정된 위치로 탈출하고 싶다 하면 break, continue문에 이름을 지정해 반복을 옮겨다닐 수 있다.
ex)
outer :
while (true) {
...
for(;;) {
...
if(num == 0)
break;
if(num == 99)
break outer;
...
} // for(;;)
} // while(true)
num에 0을 입력하면 break;가 실행되면서 다시 for문 안의 문장들을 실행할 수 있고, num에 99를 입력했을 때는 for문에서 break outer;를 통해 for문과 while문 모두를 벗어날 수 있게 된다.
Reference
남궁성. Java의 정석
-
연산자
목적
자바가 제공하는 다양한 연산자를 학습하세요.
학습할 것
산술 연산자
비트 연산자
관계 연산자
논리 연산자
instanceof
assignment(=) operator
화살표(->) 연산자
3항 연산자
연산자 우선 순위
(optional) Java 13. switch 연산자
산술 연산자
산술 연산자는 사칙연산을 다루는 기본적이면서도 가장 많이 사용되는 연산자 이다.
산술 연산자는 [ +, -, *, /, % ] 5가지가 있고 연산 순서는 { *, /, % } -> { +, - } 이다.
사칙연산자
사친 연산자 같은 경우는 산술 연산자 중 { +, -, *, / } 로 나타낼 수 있다.
그리고 사칙 연산자에선 예시로 int 형의 연산 중 10 / 4 같은경우 우리는 2.5라 생각하지만 2라고 나온다. int타입은 소수점을 정희하지 못하으몰 정수만 남고 소수점 이하는 버려진다. 그래서 올바른 연산을 위해선 한 쪽을 실수형의 값으로 바꿔 결과를 가져야 한다.
피 연산자가 정수형인 경우, 나누는 수를 0으로 사용할 수 없다. -> 컴파일 시엔 정상적이지만 실행 시 오류가 발생
이때 0을 실수형 0 인 0.0으로 나누는 것은 가능하지만 실행 시(ArthmeticException) 오류가 난다.
또 byte형 같은 경우는 연산을 시행하면 int형보다 작기 때문에 byte를 int형으로 바꿔서 연산을 수행해야 한다.
byte a = 10;
byte b = 20;
byte c = a + b;
때문에 byte c 같은 경우 int형으로 바뀐 값을 넣으려 하니 에러가 발생한다.
나머지 연산자
왼쪽 피연산자를 오른쪽 피연산자로 나누고 난 나머지 값을 결과로 반환하는 연산자로 오른쪽 피연산자를 0으로 사용할 수 없다. 주로 짝, 홀, 배수 검사등에 사용한다.
비트 연산자
비트 연산자는 피연산자를 비트단위로 논리 연산한다. 피 연산자를 이진수로 표현했을 때 각 자리를 규칙에 따라 연산을 수행하며 피 연산자로 실수는 허용하지 않는다. 정수만 허용된다.
x
y
x | y
x & y
x ^ y
1
1
1
1
0
1
0
1
0
1
0
1
1
0
1
0
0
0
0
0
(OR 연산자) : 피연산자 중 한쪽 값이 1이면, 1의 결과를 얻는다.
& (AND 연산자) : 양쪽이 1이여야 1을 결과로 얻는다.
^ (XOR 연산자) : 피연산자의 값이 서로 다를 때만 1을 결과로 얻는다.
~(전환 연산자) : 피연산자를 2진수로 표현했을 때, 0은 1로 1은 0으로 바꾼다. (보수를 얻을 때 사용)
«, » (쉬프트 연산자) : 피연산자의 각 자리(2진수를 표현했을 때)
관계 연산자
관계 연산자는 비교 연산자라 부를 수 있으며 주로 조거눔ㄴ과 반복문 조건식에 사용되며 결과는 true 아니면 false 뿐이다.
대소비교 연산자
[ <, >, <=, >= ] 두 피연산자의 값의 크기를 비교하는 연산자이다. boolean을 제외한 자료형에 다 사용할 수 있지만, 참조형에는 사용할 수 없다.
등가비교 연산자
[ ==, != ] 두 피연산자의 값이 같은지 또는 다른지 비교하는 연산자이다. 모든 자료형에 사용할 수 있고 참조형의 경우엔 객체의 주소값을 저장하기 때문에 두 개의 피연산자가 같은 객체를 가리키고 있는 지 알 수 있다.
기본형과 참조형은 서로 형변환이 가능하지 않기 때문에 등가비교 연산자(==, !=) 로 기본형과 참조형을 비교할 수 없다.
그리고 두 문자열을 비교할 땐 비교 연산자 대신 equals() 라는 메서드를 사용해야 한다. 문자열의 내용이 같은 지 비교하기 위해서이다.
논리연산자
논리 연산자는 두개의 조건이 결합된 경우는 [ &&,
] 을 사용하여 나타낸다.
x
y
x || y
x && y
true
true
true
true
true
false
true
false
false
true
true
false
false
false
false
false
논리연산자의 장점은 효율적인 연산을 한다는 것이다.OR 연산자를 예로 들면 두 피연산자 중 한쪽만 참이면 참이 되기 때문에 다른 쪽은 평가하지 않는다.
논리부정연산자
[ ! ] 이 연산자는 반대로 결과를 반환한다. 따라서 전원버튼이나 토글 버튼을 논리적으로 구현할 수 있다.
instance of
참조변수가 참조하고 있는 인스턴스의 실제 타입을 알아보기 위해 instanceof 연산자를 사용한다. 주로 조건문에 사용되며 식은 ( 참조변수 instanceof 클래스명) 이 피연산자로 위치한다. 어떤 타입에 대한 instanceof연산의 결과가 true라는 것은 검사한 타입으로 형변환이 가능하다는 것을 뜻한다.
그리고 자신의 조상 타입의 경우에도 true라는 결과를 얻는다.
Assignment(=) operator (할당 연산자)
할당 연산자는 대입 연산자랑 같은 말이며 변수와 같은 저장공간에 값 또는 수식의 연산결과를 저장하는데 사용된다.
할당 연산자는 연산자들 중에서 가장 낮은 우선순위를 가지고 있기 때문에 식에서 제일 나중에 수행된다. 그리고 연산 진행방향이 오른쪽에서 왼쪽이다.
그리고 연산자는 단순할당 연산자랑 복합 할당 연산자로 나뉘어져 있는데 단순 할당 연산자는 우리가 아는 = 이것이고 복합 대입 연산자는 나머지를 의미한다.
할당 연산자의 종류
= : 오른쪽 피연산자의 값을 왼쪽 피연산자에 저장합니다.
+= : 오른쪽 피연산자의 값을 왼쪽 피연산자와 더해서 왼쪽 피연산자에 저장합니다.
-= : 오른쪽 피연산자의 값을 왼쪽 피연산자에서 빼서 왼쪽 피연산자에 저장합니다.
*= : 오른쪽 피연산자의 값을 왼쪽 피연산자와 곱해서 왼쪽 피연산자에 저장합니다.
/= : 왼쪽 피연산자를 오른쪽 피연산자로 나눠서 왼쪽 피연산자에 저장합니다.
%= : 왼쪽 피연산자에서 오른쪽 피연산자로 나눈 나머지(모듈러스)를 왼쪽 피연산자에 저장합니다.
«= : 오른쪽 피연산자 값의 비트수만큼 왼쪽 피연산자를 왼쪽으로 이동해서 왼쪽 피연산자에 저장합니다.
»= : 오른쪽 피연산자 값의 비트수만큼 왼쪽 피연산자를 오른쪽으로 이동해서 왼쪽 피연산자에 저장합니다.
&= : 왼쪽, 오른쪽 피연산자의 비트 AND를 구해서 왼쪽 피연산자에 저장합니다.
**
=** : 왼쪽, 오른쪽 피연산자의 비트 OR를 구해서 왼쪽 피연산자에 저장합니다.
^= : 왼쪽, 오른쪽 피연산자의 비트 XOR를 구해서 왼쪽 피연산자에 저장합니다.
대입 연산자에 대한 정리 글
화살표 연산자
화살표 연산자 같은 경우는 람다에서 주로 사용되며 식별자 없이 실행가능한 함수를 가리킬 때? 매개변수들이 무엇인지 가리켜? 주려고 사용한다.
Print print = new Print(i){
System.out.println(i);
};
//윗 부분을 아래처럼 나타낼 수 있다.
Print print = (i) -> System.out.println(i);
이렇게 쓰면 장점이 가독성이 높아진다는 것입니다.
3항 연산자
3항 연산자의 경우에는 조건식 1, 2, 모두 세 개의 피연산자를 필요로 하는 삼항 연산자이며, 조건 연산자 중 하나뿐이다.
조건식 ? 식1 : 식2
조건 연산자의 결과가 true이면 식1이, false 이면 식2가 연산결과가 된다.가독성을 높이기 위해 괄호를 사용하기도 한다.
일단 이것의 장점은 if문에 있어 조건연산자가 간략하다는 것이다.
연산자 우선순위
산술 > 비교 > 논리 >대입, 대입은 제일 마지막에 수행된다.
단항(1) > 이항(2) > 삼항(3), 단항의 연산자의 우선순위가 이항 연산자보다 높다.
단항 연산자와 대입 연산자를 제외한 모든 연산의 진행방향은 왼쪽에서 오른쪽이다.
종류
결합규칙
연산자
우선순위
단향 연산자
<—
++, –, +, =, ~, ! (type)
높음
산술 연산자
—>
*, /, %
—>
+, -
—>
«, »
비교연산자
—>
<, >, <=, >=, instanceof
—>
==, !=
논리연산자
—>
&
—>
^
—>
|
—>
&&
—>
||
삼항연산자
—>
? :
대입 연산자
<—
=, +=, -=, *=, /=, %=, «=, »=, &=, ^=, |=
낮음
-
primitive 타입, 변수, 배열
목적
자바의 프리미티브 타입, 변수 그리고 배열을 사용하는 방법을 익힙니다.
프리미티브라는 한글말만 보고 커피에 타먹는 가루 용어인가 그래서 어원 비슷하게 쓰는가 생각했는데.. 검색해보니 ‘primitive = 기본적인, 원시적인’ 이라는 뜻이라니.. 잠깐 누구도 보지 않았는데 영어가 부족한 내가 부끄러운 순간이었다..
학습할 것
프리미티브 타입 종류와 값의 범위 그리고 기본 값
일단은 정신을 다잡고 primitive 타입을 알아보기 전 일단 변수의 타입에 대해서 알아보자
우리가 사용하는 값은 문자와 숫자로 나뉘어지고 값의 종류에 따라 저장될 공간의 크기와 저장형식을 정의한 것이 자료형이다. 자료형에는 문자형, 정수형, 실수형이 있으며 가장 알맞은 자료형을 변수의 타입으로 선택하면 된다.
자료형은 기본형(프리미티브 타입)과 레퍼런스 타입으로 나뉘는데 이 둘의 비교는 나중에 하고 프리미티브 타입에 대해서 알아보자
프리미타입은 모두 8개의 타입이 있으면 논리형, 문자형, 정수형 실수형으로 구분된다.
프리미티브 타입 종류, 기본값
분류
타입
논리형
booleantrue와 false 둘 중 하나의 값으로 갖으며 조건식과 논리적 계산에 사용된다.
문자형
char문자를 저장하는 데 사용되며 변수에 하나의 문자만 저장
정수형
byte, short, int, long정수를 저장하는데 사용되며 주로 int가 사용된다. byte는 이진 데이터 다룰 때 사용되며, short는 C언어와 호환을 위해서 추가되었다.
실수형
float, double실수를 저장하는데 사용되며, 주로 double이 사용된다.
Primitive type 범위
1byte
2byte
4byte
8byte
논리형
boolean
문자형
char
정수형
byte
short
int
long
실수형
float
double
값의 범위
자료형
저장 가능한 값의 범위
bit
byte
boolean
false, true
8
1
char
‘\u0000’ ~ ‘\uffff’ (0 ~ 2^16 -1, 0 ~ 65535)
16
2
byte
-128 ~ 127 (-2^7 ~ 2^7-1)
8
1
short
-32,768 ~ 32,767 (-2^15 ~ 2^15 -1)
16
2
int
-2147483648 ~ 2147483647
32
4
long
-9223372036854775808 ~ 9223372036854775807
64
8
float
3.4E-38(-3.410^38) ~ 3.4E+38(3.410^38)
32
4
double
1.79E-308(-1.7910^308) ~ 1.79E+308(1.7910^308)
64
8
프리미티브 타입과 레퍼런스 타입
프리미티브가 기본형이라고 하면 레퍼런스는 그래도 참조형이라고 알고 있다! 여튼 위에 프리미티브 타입에 대해 적었기 때문에 레퍼런스 타입에 대해서 이야기 하자면, 레퍼런스 타입은 어떤 값이 저장되어 있는 주소(memory address)를 값으로 갖는다. 즉 주소를 따라가 적혀있는 값을 사용한다고 생각하면 된다.
참조형 변수를 선언할 때는 변수의 타입을 클래스로 사용해 클래스 이름이 참조변수의 타입이 된다. 즉 새로운 클래스는 새로운 참조형을 추가한다 봐도 된다.
Date today = new Date();
여기서 이 코드의 뜻은 Date 객체를 생성해서 그 주소를 today에 저장이라고 하면 된다.
그리고 참조변수 today를 통해 생성된 객체를 사용할 수 있게 된다.
여기서 프리미티브랑 레퍼런스 차이를 조금 더 추가해서 이야기 하자면, 프리미티브는 date type을 쓰며 레퍼런스의 참조형은 항상 객체의 주소(4byte 정수)를 저장하여 객체의 종류에 의해 구분되므로 type이라는 용어로 사용한다. 허나 타입이 자료형보다 포괄적 의미이므로 굳이 구분할 필요는 없다.
리터럴
리터럴은 일단 상수랑 비교해야 하는데 상수는 프로그래밍에선 값을 한번 저장하면 변경할 수 없는 고유 값이라고 생각하면 된다. ex) final int WIDTH = 20;
그래서 리터럴이 무엇이냐 하면 데이터 그 자체를 말한다. 즉, 위 WIDTH 예시에서 20의 값을 final로 넣은 WIDTH는 상수이고 저 20 이라는 값은 우리가 20이라는 뜻을 세상을 바꾸지 않는 한 변하지 않는 값이 ‘리터럴’이라고 말할 수 있다.ex) 1L (Long타입으로 지정하는 숫자 1)
재미있는건 리터럴의 타입은 저장될 변수의 타입과 일치하는 것이 보통이지만, 타입이 달라도 저장 범위가 넓은 타입에서 좁은 타입으로 저장은 허용된다는 것이다. ex) int i = ‘A’;
변수 선언 및 초기화하는 방법
일단 변수에 대해 뜻을 한번 적어보자면 ‘단 하나의 값을 저장할 수 있는 메모리 공간’ 이라고 정의한다. (그냥 일단 적어봤다)
변수 선언
int age;
이렇게 코드에 적어주면 변수를 선언했다고 말한다.
저기서 int 라고 적어 준 것이 변수 타입이며 저장하고자 하는 값에 맞게 타입을 선택해서 적어주면 된다. 그다음 age 라고 적어 준 것이 변수 이름이며 뜻에 적어놓은 메모리 공간에 붙여준 이름이라고 생각하면 된다. 저 age로 가져오고 저장하는 일을 할 수 있다.
변수의 초기화
변수의 초기화란, ‘변수를 사용하기 전에 처음으로 값을 저장하는 것’ 이며, 뜻 처럼 변수를 선언한 후엔 변수 사용을 할 수 있으나 그 전에 변수를 초기화 해주어야 한다. 메모리는 여러 프로그램에 의해 저장된, ‘알 수없는 값’이 남아 있을 수 있기 떄문이다.
int age = 25;
변수의 종류에 따라 초기화를 생략할 순 있지만, 사용되기 전엔 값을 초기화 시켜주는 것이 좋다.
변수 의 스코프와 라이프타임
스코프
스코프란 뜻은 잘 몰라서 검색해보니 영역이라는 뜻을 가지고 있다. 영역이면 흠.. 백기선님이 말한 변수의 스코프 = 변수의 영역 이면 변수가 쓰이는 공간.. 그러면 한 공간에서 쓰이는 변수의 공간, 영역이면 기본 클래스를 배울 때 class에 선언한 변수는 class 안의 함수에서 쓸 수 있지만, class 내 method 에 선언한 변수는 클래스 밖으로 가져 올 수없다. 이런건가 라고 생각한다.
그리고 이젠 검색해서 찾아보면, 예상이랑 비슷하게 자바 변수가 가질 수 있는 활동 영역이 있고 이걸 스코프라 부른다. 스코프의 종류는 메소드 스코프랑 클래스 스코프로 나뉜다.
메소드 스코프 : 메소드 내에서 선언해 메소드 내에서만 돌아다니는 영역
클래스 스코프 : 클래스 내부에 있는 곳을 전부 다 돌아다닐 수 있는 영역
예를 들어 account 클래스가 있고 생성자를 만들어 주려고 한다. (이름을 넣어주는 생성자)
public Class Account{
String name;
public Account(String name){
name = name;
}
}
이런 식으로 되어 있으면 생성자 내에 있는 name = name; 부분을 보자. 앞의 name은 사실 우리가 원하는 Account 클래스 내의 name에 파라미터로 받아온 값을 클래스 스코프의 name에 넣고 싶은데 저렇게 쓰면 name을 쓰면 파라미터에 파라미터를 넣게 되어버린다. 왜냐면 같은 메소드 스코프 내에서의 name에 name 을 넣겠다는 뜻이기 때문에
우리는 이때 써야할 것은 this가 있다. 이 this라는 것은 자기 자신 객체를 가리키는 키워드로써
this.name = name;
라고 적어주면 우리가 원하는 방식대로 name을 넣을 수 있다.
라이프타임
라이프타임은 내가 구글링이 부족한 지 정보를 잘 알아 낼 수 없어 남들이 올린 과제를 좀 읽어보기로 했다. (센세.. 나 잘하고 있는거 맞죠?) 일단은 여러 글들을 읽어보니 데이터가 살아 있는 시간, 즉 가비지 컬렉터가 돌아가기 전까지의 시간이라고 써져있다. 그리고 라이프 타임이 적용되는 3가지 변수가 있는데 인스턴스 변수, 클래스 변수, 지역변수가 있다.
변수
범위
생성시기
종료시기
클래스 변수
클래스
클래스 메모리 선언시
프로그램 종료 시
인스턴스 변수
클래스
인스턴스 생성
GB 실행 시
지역 변수
메서드, 생성자, 블럭
변수 실행
선언된 범위 넘어갈 때
타입 변환, 캐스팅 그리고 타입 프로모션
타입변환
‘하나의 타입을 다른 타입으로 바꾸는 것’ 이라고 한다. 자바에서는 논리형 boolean을 제외한 나머지 기본 타입 간 타입변환이 자유롭게 된다.
여기서 하나 주의할 것은 다른 타입끼리의 연산을 수행할려고 하면 피 연산자들을 같은 타입으로 만들어 진행합니다. 그래서 메모리에 할당받은 바이트 크기가 상대적으로 작은 타입에서 큰 타입으로 변경은 생략할 수 있다. 허나 반대인 경우에는 큰 것을 작은 것에 넣다 보니 데이터 손실이 일어난다는 것이다. 따라서 이 때는 자바 컴파일러가 오류를 발생시킨다.
캐스팅
프로그래밍시 가른 타입간의 연산을 수행해야 하는 경우가 있는데, 이때 타입을 일치시켜야 하는데 이거를 캐스팅(형변환)이라고 한다.
방법은 간단히 ‘(타입)피연산자’ 로 정의하며 피 연산자를 타입형으로 바꿨을 땐 값만 변해서 읽을 뿐, 변화는 없다.
그리고 기본형과 참조형은 서로 형변환을 할 수 없다.
타입 프로모션
타입 프로모션.. 이럴수록 영어에 대해 참 막막해질 때가 있다. 처음엔 오 새로운건가 했다가 자동 형변환이라는 해석을 듣고 당황;; 허나 일단은 이걸 적어놓으면 담엔 안헷갈리겠지 하며 적는다.
타입 프로모션은 한국말로 자동 형변환라고 불리며 편의상의 이유로 형변환을 생략하는데 진짜 생략이 아니라 컴파일러가 생략된 형변환을 자동으로 추가해준다.
근데 재밌는건 명시적으로 형변환 해줬을 경우는 의도적인 것을 알고 컴파일러는 에러를 발생시키지 않는다.
char ch = (char)1000;
일단 컴파일러는 자동적으로 더 넓은 타입으로 형변환 해준다.
즉 타입 프로모션의 규칙은 ‘기존의 값을 최대한 보존할 수 있는 타입으로 자동 형변환 해준다.’ 이다.
1차 및 2차 배열 선언하기
배열은 같은 타입의 여러 변수를 하나의 묶음으로 다루는 것 이라고 나온다.
그리고 1차원 선언 방법은
타입[] 변수이름; //배열 선언(배열을 다루기 위한 참조변수 선언)
변수이름 = new 타입[길이]; // 배열을 생성( 실제 저장공간 생성)
2차원 선언 방법은
타입[][] 변수이름; // int[][] score;
타입[] 변수이름[]; // int[] score[];
타입 변수이름[][]; // int score[][];
초기화는 첫번째 [ ] 은 ‘행’ 이고 두번째 [] 은 ‘열’ 이다.
int[][] score = new int[4][2];
타입 추론, var
타입 추론이란, 코드 작성 당시엔 타입이 정해져 있지 않았지만, 컴파일러가 그 타입을 유추하는 것이다.
이는 곧 타입을 명시하지 않아도 되며 가독성을 높일 수 있다.
그리고 var 이거 자바스크립트에서 보던 것인데 java에서 보인다 ( 아직 난 개발자가 되려면 멀었나 보다..)
찾아보니깐 java 10 에서부터 지원하는 변수로 선언할 수 있게 되었다. 그리고 11부터 이를 이용한 람타타입 지원도 생겼다 한다. 그리고 javascript에서 var랑 비슷하면서 다른데 java에서의 var는 종속적인 강타입으로 관리하기 때문이라고 한다. 또한 var는 선언과 동시에 초기화 해줘야 한다. 나는 var 같은 경우는 클래스 타입이 제네릭으로 해서 길거나 값이 복잡해지면 var 쓰고 설명만 잘해놓으면 괜찮게 사용할 수 있다 생각한다. (근데 여긴 내 생각 쓰는데가 아닌데;;) 일단은 백기선님 스터디 들으면서 var에 대한 설명이 나오면 잘 들어봐야겠다.
-
자바의 애노테이션
목적
자바의 애노테이션에 대해 학습하세요.
학습할 것
애노테이션 정의하는 방법
@retention
@target
@documented
애노테이션 프로세서
1. 애노테이션 정의하는 방법
애너테이션이란
프로그램의 소스코드 안에 다른 프로그램의 위한 정보를 미리 약속된 형식으로 포함시킨 것
프로그래밍 언어에 영향을 미치지 않으면서도 다른 프로그램에게 유용한 정보를 제공하는 장점
작성법
@interface 애너테이션이름 {
타입요소 이름();
...}
@애너테이션이름 -> 애너테이션
애너테이션이름 -> 애너테이션 타입
애너테이션 내에 선언된 메서드를 애너테이션의 ‘요소’ 라고 하며 요소들을 통해 값을 정의할 수 있다.
요소들은 반환값이 있고 매개변수가 없는 추상 메서드 형태이며, 상속을 통해 구현하지 않아도 된다.
다만 요소들의 값은 빠짐없이 지정해줘야 한다. (default를 옆에 붙여주면 기본 값을 가질 수 있다.)
@interface TestInfo{
String[] testTools() default {"test1", "test2", ""}
}
@TestInfo(testTools = {"aaa", "bbb", "ccc"})
class or method { ~~ }
만약 이름의 요소가 value 이면 요소이름 생략 가능
java.lang.annotation.Annotation
애너테이션은 상속이 허용되지 않으므로, 명시적으로 Annotation을 조상으로 지정할 수 없다.
2. @retention
애너테이션이 유지되는 기간을 지정하는데 사용한다.
유지정책
의미
SOURCE
소스파일에만 존재. 클래스파일에는 존재하지 않음
CLASS
클래스파일에 존재. 실행시에 사용불가. 기본값
RUNTIME
클래스파일에 존재. 실행시에 사용가능.
컴파일러가 사용하는 애너테이션은 유지 정책이 SOURCE이다.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override { ...}
SOURCE : 컴파일러가 사용하는 애너테이션, 직접작성할 것 아니면 이 유지정책은 필요 없다.
RUNTIME : , 실행 시 리플랙션을 통해 클래스파일에 저장된 애너테이션 정보를 읽어서 처리할 수 있다.
CLASS : 클래스 파일에 저장할 수 있게는 하지만 클래스파일이 JVM 로딩될 때는 정보가 무시되어 애너테이션 정보를 얻을 수 없다.
3. @target
애너테이션이 적용가능한 대상을 지정하는데 사용.
대상타입
의미
ANNOTATION_TYPE
애너테이션
CONSTRUCTOR
생성자
FIELD
필드
LOCAL_VARIABLE
지역변수
METHOD
메서드
PACKAGE
패키지
PARAMETER
매개변수
TYPE
타입
TYPE_PARAMETER
타입 매개변수
TYPE_USE
타입이 사용되는 모든 곳
TYPE은 타입을 선언할 때 붙일 수 있고, TYPE_USE는 해당 타입의 변수를 선언할 때 붙일 수 있다.
FIELD는 기본형에, TYPE_USE는 참조형에 사용된다.
4. @documented
애너테이션에 대한 정보가 javadoc으로 작성한 문서에 포함되도록 한다. 자바에서 제공하는 기본 애너테이션 중, @Override, @SuppressWarnings를 제외하고는 이 메타 애너테이션이 붙어있다.
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface{}
5. 애노테이션 프로세서
어노테이션을 프로세싱 즉, 내가 보았던 것은 lombok이 대표적이며, 이 외에 이것을 어떻게 작성해야 될 지 모르겠다. 그래서 강의를 들으면서 작성하려고 한다.
Reference
남궁성. Java의 정석
-
JVM, 자바 코드 실행 과정
목적
자바 소스 파일(.java)을 JVM으로 실행하는 과정 이해하기
남궁성님이 출판하신 JAVA의 정석를 기반으로 작성하려고 한다.
학습할 것
JVM이란 무엇인가
JVM이란 java를 실행하기 위한 가상 머신이다. 여기서 ‘가상머신’ 대신 ‘가상 컴퓨터’라고 부르는게 이해하기 좋다.
자바로 작성된 애플리케이션은 모두 이 가상 컴퓨터에서 실행되기 때문에 Java Application이 실행되기 위해선 반드시 JVM이 필요하다.
일반 Application의 코드는 OS만 거쳐 하드웨어로 전달되는데 Java Application은 JVM을 한 번 더 거치며 실행 될 때 하드웨어에 맞게 컴파일을 진행한다.
그렇다고 JVM 하나로 통합되는게 아니라 각 OS에 맞는 JVM이 실행된다 (ex : Window용 JVM, Macintosh용 JVM, Linux용 JVM )
이래서 자바는 Write once, run anywhere (한번 작성시 어디서든 실행된다.) 라는 장점이 있다.
컴파일 하는 방법
개발 툴을 이용해 .java 파일을 생성하고 build시 java Compiler의 javac라는 명령어를 사용해 .class 파일을 생성합니다. (이 .class는 파일은 아직은 어셈블리어가 아닌 자바 바이트 코드 입니다)
이 파일은 클래스 로더에 의해서 JVM 내로 로드되고 Execution Engine에 의해 기계어로 해석되어 메모리에 배치되게 됩니다. 그리고 Stack Area, Method Heap Area, PC Register Area 에 올라간 파일들은 클래스 메소드 호출이 발생하면 영역의 Method들 정보를 읽어 매개변수, 지역변수 리턴값등이 Stack에 의해 처리되게 됩니다. 그리고 메소드 실행 끝나면 Stack영역에서는 자동으로 제거됩니다. 이후 Garbage Collector가 실행되어 메모리를 정리해주게 됩니다.
바이트코드란 무엇인가
자바의 바이트 코드란 JVM이 이해할 수 있는 언어로 변환된 자바 소스 코드이며, 자바 컴파일러에 의해 변환되는 코드의 명령어 크기가 1바이트라서 자바 바이트 코드라고 불리고 있다.
바이트 코드의 확장자는 .class이며 가상머신만 설치되어 있다면, 어떤 운영체제에서라도 실행될 수 있습니다.
JIT 컴파일러란 무엇이며 어떻게 동작하는지
일단 JIT(Just In Time) 컴파일러란 동적 번역 이라 할 수 있으며 실제로 프로그램을 실행하는 기점에서 기계어로 번역하는 컴파일러이다.
여기서 원래 컴퓨터 프로그램을 만드는 방법은 2가지가 있는데 인터프리트 방식과 컴파일 방식으로 나눌 수 있다.
Interpreter는 실행 중 프로그래밍 언어를 읽어가면서 해당 기능에 대응하는 기계어 코드를 실행한다.
반명 정적 compile은 실행하기 전에 프로그램 코드를 기계어로 번역한다.
여기서 JIT는 두가지를 혼합한 방식이라고 볼 수 있는데 실행시점에서 Interpreter 방식으로 기계어 코드를 생성하면서 그 코드를 ‘캐싱’하여 같은 함수가 여러번 불릴 때 캐싱된 데이터를 사용해 매번 기계어 코드를 생성하는 것을 방지한다.
이래서 한번만 실행되는 코드는 Interpreter 방식이 유리하며 JVM은 메소드가 얼마나 자주 수행되는 지를 체크하여 컴파일을 수행한다.
JVM 구성 요소
JVM은 ClassLoader, Execution Engine, RuntimeData Areas로 구성된다.
ClassLoader 란
JVM 내로 클래스 파일을 load하고 link를 통해 배치를 수행하는 모듈로 Runtime 시에 동적으로 클래스를 로드한다.
자바는 동적코드, 컴파일 타임이 아니라 런타임에 참조한다.
즉 ,클래스를 처음으로 참조할 때 해당 클래스를 load하고 link한다.
Execution Engine 란
class loader를 통해 배치된 클래스를 실행시킨다.
클래스 파일(바이트 코드)은 비교적 인간이 보기 쉬운 형태이기 때문에 기계가 실행할 수 있는 형태로 변경시키는데 이때, JIT와 Interpreter 두가지 방식을 사용한다.
Garbage Collector
동적으로 할당된 메모리 중 사용되지 않은 메모리를 반환하다.
실행 시기는 JVM이 OS에 메모리를 추가적으로
Runtime Data Area
Excution Engine에게 해석된 프로그램은 Runtime Data Area에 배치되어 돌아간다. 이 때 Runtime Data Area는 3가지 구조를 갖는다.
Method Area : 클래스영역으로 인터페이스, 클래스, 메소드, 필드 등의 모든 정보가 들어가 있다. 여기는 모든 쓰레드들이 공유하며 new를 통해 동적으로 생성 시 저장되므로 Garbage Collector 의 대상이다.
Heap : 생성된 인스턴스가 있는 공간으로 Garbage Collector의 대상이며 Heap 정렬을 통해 사용빈도가 높은 인스턴스를 Root Node로 올려서 히트율을 올리는 방식이다. 모든 쓰레드들이 공유한다.
JVM Stacks : 호출스택이라 불리며 메소드가 실행 시 필요한 공간을 제공, 매개변수나 지역변수 등의 임시데이터를 저장한다. 메소드가 끝나면 메모리공간은 반환된다. 쓰레드마다 1개 씩 갖는다.
PC Registers : 일종의 스택으로서 Thread 생성될 때의 공간으로 Thread가 어떠한 명령을 실행하게 될지에 대한 부분을 기록한다. Java는 Stacks-Base 방식으로 작동하는데 JVM은 CPU에 직접 Instruction을 수행하지 않고 Stack에서 Operand를 뽑아내 이를 별도의 메모리 공간에 저장하는 방식을 취하는데 이 공간을 PC라고 한다.
JDK와 JRE의 차이
JDK
자바 개발을 위해 필요한 도구, JRE에서 제공하는 실행 환경뿐만 아니라 자바 개발에 필요한 여러가지 명령어 그리고 컴파일러를 포함했다.
JRE
Java Runtime Environment 의 약자로 자바 실행환경이다. JRE는 자바 프로그램을 동작시킬 때 필요한 라이브러리 파일, java 명령, 및 기타 인프라를 포함하여 컴파일된 Java 프로그램을 실행하는데 필요한 모든 패키지
-
-
-
-
SSR, SSG
SSR, SSG, jamstack
SSR
Server Side Rendering으로 서버에서 HTML 구조로 응답하는 방식
CSR
Client Side Rendering (ex Angular, React, Vue) javascript로 동적 페이지를 출력
SPA
Single Page Application으로 한 페이지 내에서 모든 동작을 수행 ( CSR과는 다른 개념)
SEO (Search Engine Optimization)
검색 페이지 적합하게 만들어진 페이지, 크롤링을 통해 구조, 컨텐츠 파악해 목차와 색인을 만드어 사용자 검색의 결과물을 나타낸다.
Next.js, Nuxt.js
Next.js는 React, Nuxt.js는 Vue에 각각 두개의 장점을 가져오는 ssr로 페이지를 제공하는 기능들을 추가 즉( CSR을 통해 비 동기인 방식으로 요청했을 때, SSR의 장점인 html 구조로 응답하는 방식
Vue를 통해 만든 프로젝트를 요청했을 때, 처음에 div만 들어있는 페이지를 호출하고, 브라우저의 요청이 실행되었을 때, 링크된 자바스크립트 파일을 다운로드 뒤 데이터를 요청해서 서버에 받아온 데이터들을 클라이언트에서 모양을 생성해 출력한다. 이때 요청이나 로직이 많은 경우라면 시작 시에 딜레이가 발생한다.
허나 Next.js나 Nuxt.js를 사용하면 기존에 노출시키고 싶은 페이지와 데이터를 mounted()를 통해 먼저 렌더링 한 후, fetch 코드를 사용하여 변화되는 데이터를 API 서버에 요청해 받아오는 것이다. node js가 깔린 서버에서 프로젝트를 어플리케이션으로 실행해 프론트를 배포한다. 즉, 페이지에서 접속시 바로 출력할 내용을 서버에 미리 렌더링 해 출력한다.
또한 mounted를 통해 먼저 렌더링했을 경우 SEO에 잡힐 수 있기 때문에 검색결과에 노출 비중이 높다.
SSG
Static Site Generator, 정적 사이트 생성기, -> 렌더링을 다 마친 정적 파일들을 배포,
정적인 사이트를 만들 때엔 json, yaml, xml등의 view 파일에 저장하고 build 시 html 태그 구조에 맞게 수정한 html 파일을 서버에 저장시켜 놓는다. (Next, Hugo, Gatsby)
Jam Stack
Javascript API Markup + Stack 으로 웹서비스 만드는 설계 방식,
pre-landering, decoupling
자바 스크립트를 사용한 Jam으로 페이지를 Pre 렌더링 후 외부 API 서비스로 아웃소싱해서 설계 분리(decoupling)
이렇게 만든 정적 페이지를 CDN으로 배포하는 방식이다. 이걸 통해 비용절감, 보안강화, 사이트 확장에 용이하다.
-
PostgreSQL이란
PostgreSQL
오픈 소스 객체 관계형 데이터베이스
새로운 하나의 프로그래밍처럼 구현하는 기능을 제공한다.
구조
클라이언트 / 서버 모델 사용
기능
트랜잭션과 ACID(Atomicity, Consistency, Isolation, Durability)를 지원
또한 주요 기능들을 많이 가지고 있다고 한다.
Nested transactions (savepoints)
Point in time recovery
Online/hot backups, Parallel restore
Rules system (query rewrite system)
B-tree, R-tree, hash, GiST method indexes
Multi-Version Concurrency Control (MVCC)
Tablespaces
Procedural Language
Information Schema
I18N, L10N
Database & Column level collation
Array, XML, UUID type
Auto-increment (sequences),
Asynchronous replication
LIMIT/OFFSET
Full text search
SSL, IPv6
Key/Value storage
Table inheritance
등등 여러 기능들을 제공한다고 하는데 여기서 Auto-increment나 Limit 등등 빼고는 잘 접하지 않아서 그런지 이해는 못하겠다. 나중에 PostgreSQL을 좀 더 많이쓰는 일이 생기면 접할 것 같다.
특징
유연한 객체 생성
단순한 자료 저장소로써의 기능을 넘어 마치 하나의 새로운 프로그래밍 언어처럼 개발자의 창의성에 따라 무한한 기능을 손쉽게 구현할 수 있도록 한다.
상속
java 또는 C++ 프로그래밍 언어와 같이 테이블을 만들어 그 테이블 상속 기능을 이용해 하위 테이블을 만들 수 있다. 테이블에 저장된 자료는 상위 테이블을 조회하면, 해당 테이블의 하위 테이블에 포함된 모든 자료를 조회할 수 있으며, 하위 테이블을 만들 때, 상위 테이블의 칼럼을 그대로 상속 받으면서, 하위 테이블에만 속하는 칼럼을 추가로 만들 수 있다.
함수
때때로, ‘저장 프로시저’라고 불리는 SQL문으로 작성된 함수를 서버환경에서 사용할 수 있다. 비록 다른 언어와는 달리 제어문과 반복문을 사용하지는 못하지만, 다른 언어와 결합시킬 수 있다. 일부 언어에서는 심지어 트리거 내부에서 실행시킬 수 있다.
내 생각
일단은 이러한 장점들이 있지만 현재 프로젝트에선 JPA를 활용하여 진행하므로 PostgreSQL의 장점을 활용할 기회가 있을지는 잘 모르겠다. 차후 postgresQL의 특징을 사용하는 경험이나 정보를 듣는다면 추가로 적용하겠다.
Touch background to close