※ Keep in mind
본 내용은 웹개발과정의 강의 중 내용을 복습을 위해서 메모한 것에 불과한 것입니다. 이러한 연유로 강의내용을 오인한 나머지 오기재 및 불기재가 있을 수 있으니 '참고'만 해주시길 바랍니다. 저의 경우에도 본 내용을 단순하 읽은 것이 결코 저의 것이라고 생각하지 않습니다. 본 내용은 복습를 위한 초기 내지 중간 과정에 불과한 것이고, 이후에 내용을 보충 후 인출 및 설명하기 과정이 있어야 비로소 복습의 단추가 어느정도 마무리 되어간다고 볼 수 있습니다.
따라서 당초에 본 내용은 비공개였습니다. 그럼에도 불구하고 본 내용을 공개한 점은 함께 공부하는 동료들과 나눔을 바탕으로 배움과 성장의 공진화라는 소기의 목적을 달성에 어느정도 도움이 될수 있기 때문이라고 생각합니다.
I. 웹개발
1. 들어가며
1)
JDK범주 여러가지야 자바 EE는 SE에다가 추가된것이야
두개 차이는?
SE은 어떤범주일까? local application만들수있는 환경을 제공하는거야
EE는 enterprise는 분산형 application과 연관되어있어
원격의 클라이언트와 반대 서버사이 application을만들수 있게끔한거야!
네트워크는 기본이고, 스탠다드의 소켓 외에도 다른 것도 가지고있어!
분산형 application의 최근버전은 다 웹개발이야
엔터프라이즈가 웹용 외 에디션의 기능은 뭐가있어? : DI기능, 트랜잭션 처리기능, 웹 프론트컨트롤러기능
엔터프라이즈 에디션으로 하던기능을 스프링으로 대체하는거야!
스탠다드에다가 엔터프라이즈얹져서 만드는거도 있고, 엔터프라이즈안쓰고 스프링으로 쓰는 방법이있어
우리가 쓰는건 스프링이야 (유료때문에)
JDK자체도 마찬가지야 자바개발자가 유료때문에 openJDK를 썼어 (생각보다 좋았어)
2)
일단 엔터프라이즈는 분산형 application 이라는 것과 역할분담을 한다는게 필요해
역할을 나누면 레이어가 나누고, 나누면 DI가 필요해 (왜? 결합력 낮추려고)
웹개발시 필요능력에서 서블릿기반 컨트롤러이고, 이를 몰라도 쓸 수있는 POJO에 대한 프론트 컨트롤러도 스프링이 제공해
3)
dao 레이어 만들 때 만드는 JDBC API 즉 JDBC로 쿼리를 싶게 가능해
dao는 우리가 만들어야하는데, 만들어주는게 생겼어 이게 mybatis가 생겼어
interface만 정하면 mybatis가 구현해주는거야!
다오 interface에다가 시퀄만 연결해주면해주는게 mybatis야
시퀄만드는데 조건처리해야하는 경우가 발생해
데이터 인자에 따라서 쿼리문의 특정인자가 필요할 수도 아닐 수도 있어!
심지어 컬럼명 달라지는것 위해 별칭쓸수도있지만, 매핑하는 resultMap하는 등도 가능해
config하는등도 부트를 쓰면 내가 할게 점점 줄어들어
4)
DI 스프링
Dao구현은 mybatis이고
기업용의 기본 라이브러리들이야
-> 이제 서비스레이어를 살펴보자
이거는 사용자 요구에 대해서 실제로 업무일은 서비스가 하는거야 컨트롤러는 입출력에 불과해
2. Spring Transaction
(1) 트랜잭션이란
1)
트랜잭션이란 하나의 업무단위 혹은 논리적인 실행단위야 (논리적이라는건 실존하지는 않는다는거야)
실제적으로 이런명령어를 가진 시퀄을 조합해야 실존해
업무라는 지시가 웹으로 요청이 와 이 업무지시가 계좌이체로 오는데, 이런 업무지시는 시퀄로 올 수없어
시퀄에 계좌이체가 없어 그냥 개념적인 것이야! 논리적인거야! 업무적인가야!
2)
이거를 실제로 구현하려면 시퀄에서 구현할 수있는 단위를 찾아야해
어떤 시퀄명령어를 찾아야하는거야? update 2번이 같이 엮여야만 개념단위 명령어를 실행할수있는거야
이게 물리명령어야 물리의 반대가 논리라는걸 기억하자!
물리적인걸 두개 엮어야, 논리적 업무적인거 실행이가능해
문제는 물리적으로 나누어졌을 때 물리적으로 나누어진게 하나만 실행되는게 문제야
두개이상이 한번에 실행되게해주는처리가 바로 트랜잭션 처리야
트랜잭션 처리를한다는건 논리적인명령어가 현존하지않는데, 하나의 단위처럼 실행하는거야
트랜잭션 처리는 정상적으로 진행되게하는거고, 정상적으로 처리안되는 상황이있다는거고 이게 4가지나 있어!
(2) 트렌젝션이 정상적으로 안되는경우
1) 4가지 경우
트랜잭션이 정상으로 안되는경우 4가지는 무엇일까?
ACID(원자성, 일관성, 고립성, 영속성)
(3)원자성
1) 원자성 : 하나를 의미해 원자는 깨지지 않는다는거야, 두개의 명령어가 하나처럼 실행되거나 원래대로 복구하는거야!
일단 원자성은 스프링인 깔끔하게 구현해줘!
2) 실습1
- 우선 interface에 업무적인단위로 이름을 지어주자
public interface MenuService {
List<Menu> getList();
// 서비스는 업무적인 단위로 이름을 지어주어야해
// 그래서 update보다는 다른거하자
void pointUp();
}
그래서 update()라는 이름보다는 pointUp()으로 명명한다
@Service
public class DefaultMenuService implements MenuService {
@Autowired
private MenuRepository repository;
public void setRepository(MenuRepository repository) {
this.repository = repository;
}
@Override
public List<Menu> getList() {
return repository.findAll(0,10,"",1,3000,"regDate","desc");
}
@Override
public void pointUp() {
Menu menu = new Menu();
menu.setId(774);
menu.setPrice(8000);
repository.update(menu);
}
}
pointUp()에 대한 구현체를 만들어준다.
3) 실습2 : MySQL에서 원자성을 위해 제약조건을 넣어주자
다만 제약조건을 넣을 때 이미 조건에 부합하지 못하면은 제약조건에 설정할 수가 없다
@Override
public void pointUp() {
Menu menu = new Menu();
menu.setId(774L);
menu.setPrice(8000);
repository.update(menu);
menu.setId(774L);
menu.setPrice(30000);
repository.update(menu);
}
pointUp() 메소드에서 둘중에 하나만 실행되면 트랜잭션이 꺠진거야
두가지가 다 실행되어야 해
이런경우 정말로 그런지 test를 해보자
@SpringBootTest
class DefaultMenuServiceTest {
@Autowired
private MenuService service;
@Test
void test() {
service.pointUp();
System.out.println("작업완료");
}
}
이 경우 가격을 30,000원을 하면 제약조건을 위반하게 된다.
CONSTRATINT로 에러가 발생한다.
하지만 실제 DB에는 트랜잭션이 깨졌다고 하더라도 값이 8000원으로 업데이트가 되었다 (원자성 X)
다만 20,000원을 하면 해결이 된다.
4)
즉 중간만 실행이 되면안되는게 원자성인데, 이게 실제로 좀 어려워
스프링은 우리에게 깔끔하고 쉽게 할수 있게 해줘!
@Override
public void pointUp() {
Menu menu = new Menu();
menu.setId(774L);
menu.setPrice(8000);
repository.update(menu);
menu.setId(774L);
menu.setPrice(20000);
repository.update(menu);
}
두개의 명령어가 실행되려면 다하던가, 안하려면 하나만하던지
원자성이 중요해!
@Transactional
@Override
public void pointUp() {
Menu menu = new Menu();
menu.setId(774L);
menu.setPrice(8000);
repository.update(menu);
menu.setId(774L);
menu.setPrice(30000);
repository.update(menu);
}
Anotation 붙여주면 다 해결이 돼!
이게 있기 전에는 제약조건(30000)을 위반해도 8000원으로 update가 돼
Anotation이 있으면 8000원이 안되고 그대로 유지가 돼!
트랜잭션 유지되려면 연결 안끊고 커밋되어야해
연결이 어떤조합으로 이루어질지 모르는걸 한다는게 보통이 아니야!
3. AOP란
이렇게 트랜잭션 어노테이션으로 하는것과 관련해서 AOP를 보자!
(1) 들어가며
OOP와 느낌이 비슷한데, 여기서 oriented는 기반이라는 뜻이야
프로그래밍은 오브젝트로 이루어진다는거야
AOP는 Aspect로 이루어진다는 의미야
object를 대신하는게 아니고 object기반에서 Aspect가 추가된거야
Aspect가 무엇이고 어떻게 추가되는지 이해를 하자!
(2) AOP 이해하기
( https://velog.io/@ann0905/AOP%EC%99%80-Transactional%EC%9D%98-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC )
1)
사용자가 요구하는것들은 일적으로 시스템으로 요청하는 사항이 업무이다
개체라는것 안에 업무를 때려넣었어 메소드로 사상시켜서
그렇게 만들어진게 개체덩어리들이고, 만들어진 덩어리를 상속이라고 해
객체를 캡슐이라는 이름으로, 조합을 상속이라는 이름으로 '캡슐조합' 을 만들어왔어
업무에 새로운것이 들어가 업무라는것은 사용자의 요구사항이야
2)
그런데 사용자가 원하는게 아닌 다른 업무가 코드로 들어오는경우가 있어
대게 CoreConcern(주관심사)이라는것을 가운데두고, 위에나 아래에 들어오는 코드(Cross Cuting Concern)야!
대게 사용자의 요구가 아니라 나(개발자)를위한코드나, 운영자를 위한 코드야
예컨데 개발할 때 log출력해보고 싶은데, 사용자가 원하는것은 아니야
사용자와 반대되는, 사용자와 다른시각을 가진사람들에 대한 업무를 Aspect(관점, 측면)라고 해!
문제는 Aspect를 코딩하는데, 방법론이 없었다가 통일된 방법론이 생겼어
왜 그런방법론이 생겼고 우리에게 fact로서 존재했냐하면 얘가 머리안픈일이 생겼어
log처리 / 보안처리/ 트랜잭션처리 등 할때
사용자는 원자성도모르고 지켜달라고한적도없고, 그냥 pointUp해달라하는거곡 지키는 건 개발자들이야!
3)
이런 코드들은 본래코드 위아래에 있어! 상황에 따라 드러낼 필요가 있어
어떤부분에서는 로그,보안 등이 보여야해 드러나는게 수시로 바뀌어야하는거야
10개함수 X 120개 하면 1200개함수하면 1200번 log를 출력해야하는데, 생산적이지도 못해
코드를 빨리 넣으라고 할 때 힘들어
이러한 곁다리 업무(실제업무는 아냐) 가 지속적으로 들어오는데, 넣었다 뺏다하는데 계속 일어나!
어떻게 하면 120번 등의 반복없이 작업가능할까? 어떨때 도킹했다가 뺄 수있을까?
그래서 등장한게 AOP이다. 곁다리는 이렇게 만드세요!
주어문은 객체지향으로 하시고, 나머지 부분은 이렇게 하세요 하는 방법론이야
곁다리를 위한 프로그래밍 방법론이야!
4)
어떻게 하면 반복을, 어떻게하면 쉽게 드러낼 수있을까 (주석 다 넣었다가 뺏다가)
이제는 그렇게 하지말자!
어떻게 하면 빨리할수있을까? onetime으로 끝내자!
우리는 core Concern (우리가 만들어야하는 주 관심사)
log출력 할 때 느려지는 함수 체크위해서 함수호출의 첫머리에다가 시작시간 찍고, 끝머리에 끝시간을 찍어서 그 차이를 로그를 남기자!
첫머리에 들어가고 끝머리에 들어가는 등으로 저게 1200개 함수를 넣어야해
관건은 보조업무와 주업무를 분리해야해! 보조가 반복이라면 분리해서 따로 둬
아이스크림이 core Concern / 빵이 cross-cutting Concern
떨어져도 결국 실행되는 흐름은 같아! 시작과 끝은 결국 같아
한번의 코딩으로서 소스코드없이도 얘를 호출해서 꽂아넣어서 가능 (ex : 어노테이션)해
이것이 바로 AOP방법론이야
가운데있는 cross-cutting Concern 를 proxy(대리)코드라고 해
쁘락치처럼 직접만나기 어려우니까 대행해서 만나는거야!
우리는 proxy를 이용을 많이 해, 남들이 보기에는 proxy인지 아닌지 모를 수 있어
proxy가 끼게되면 원래 내용을 쓸 수 있게되는데, 다양한 것을 곁들일 수있어
우리는 대리자를 많이 볼 수있을수 있어!
proxy는 원본의 이름과 동일하고, 원본인거처럼 하는데, 원본은 다른곳에 있는 등의 서비스함수야!
proxy는 그러면 어떻게 구현하는지와 관련해서 java에서도 이미 지원해
내가 하는게 원본이있으면 원본 대신하는것을 create를 하는등으로 가능해!
간단하게 spring이 제공하는 방식도 있고, java가 제공하는 방식으로 차이가 있어
(3) 자바 방식 실습
1) 기본세팅을 하자
package kr.co.rland.web.aop;
public interface Calculator {
// int plus(int x , int y); //함수가 넘겨받는거라 호출 할때 마다 값이 달라져
int plus(); //멤버값으로서 공유변수가 한번 설정하면 이래저래 쓸수있어
int sub();
int multi();
}
인자로 넘겨받는 방식보다는 공유변수로 하는것이 좋다.
package kr.co.rland.web.aop;
public class DefaultCalculator implements Calculator {
private int x;
private int y;
public DefaultCalculator() {
// TODO Auto-generated constructor stub
}
public DefaultCalculator(int x, int y) {
super();
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
@Override
public int plus() {
int result = x+y;
//여기 사이에 들가는건 사용자가 원하는건 아냐
//로그처리 트랜잭션처리 예외처리 등의 경우!
return result;
}
@Override
public int sub() {
// TODO Auto-generated method stub
int result = x - y;
return result;
}
@Override
public int multi() {
// TODO Auto-generated method stub
int result = x * y;
return result;
}
}
model 과 controller 두개를 구현한다.
2)
package kr.co.rland.web.aop;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.InvocationHandler;
public class Program {
public static void main(String[] args) {
// 1. 진짜
Calculator calc = new DefaultCalculator(3,4);
// 2. 가짜를 만들자
//프록시를 만들어주는 API를 쓰자!
Calculator 가짜 = (Calculator) Proxy.newProxyInstance(
//첫번째 실제로 가진녀석의 기능알 알려주어야해
DefaultCalculator.class.getClassLoader()
//두번째 인터페이스 목록을 넣어주자!
//구현체가 구현하고있는 인터페이스목록이야
//여러개면 다 넣어주자!
, new Class[] {Calculator.class}
//곁다리를 넣어주는거야!
//넣어주느 공간이야!
,new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
return (int) result;
}
});
int result = 0;
result = calc.plus();
System.out.printf("plus result : %d\n", result);
result = calc.sub();
System.out.printf("sub result : %d\n", result);
result = calc.multi();
System.out.printf("multi result : %d\n", result);
}
}
invoke 까지 할 수있는 세팅을 하자!
이렇게 한다. 이것을 람다식으로 바꾸어주자!
Calculator 가짜 = (Calculator) Proxy.newProxyInstance(
//1첫번째 실제로 가진녀석의 기능알 알려주어야해
DefaultCalculator.class.getClassLoader()
//2 두번째 인터페이스 목록을 넣어주자!
//구현체가 구현하고있는 인터페이스목록이야
//여러개면 다 넣어주자!
, new Class[] {Calculator.class}
//3 세번째 곁다리를 넣어주는거야!
//넣어주느 공간이야!
//람다로 바꾸어줬어
,(Object proxy, Method method, Object[] ags) -> {
// TODO Auto-generated method stub
return null;
});
메소드명, 어노테이션, 내부메소드명을 모두 지운다.
이렇게 깔끔하게 돼
3)
이제 세개의 합 빼 곱에 각자를 함수를 넣어주어야하는데 그럴필요가 이제 없어
AOP를 지원하는 플랫폼이다, library다 라고 하면 저 proxy를 만드는 API를 제공해주는거야
Proxy를 만드는 API로는 기업용에서 쓰기 어려워
왜냐하면 우리가 쓸 때 proxy로 바꿨다고 고치거나 해야하잖아!
안바꾸고 할수있는 방법으로 DI기능이 절실하게 필요해!
result = 가짜.plus();
System.out.printf("plus result : %d\n", result);
result = 가짜.sub();
System.out.printf("sub result : %d\n", result);
result = 가짜.multi();
System.out.printf("multi result : %d\n", result);
만약 바꾸면 여기도 다 가짜에서 다른 것으로 호출해주어야해
우리가 가짜라고 명명하고 코드에서는 알 필요도 없고,
DI가 원본을 던져주면 원본을 쓰는거야
스프링에 DI기능과 프록시가 만드는게 결합되면 우리는 proxy를 쓰는지 원본을 쓰는지 알길이 없어
쉽게 바꿔치기 할 수있어 코드수정없이
package kr.co.rland.web.aop;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.InvocationHandler;
public class Program {
public static void main(String[] args) {
// TODO Auto-generated method stub
Calculator 진짜 = new DefaultCalculator(3,4);
Calculator 진짜2 = new DefaultCalculator(5,6);
//가짜를 만들자
//프록시를 만들어주는 API를 쓰자!
Calculator 가짜 = (Calculator) Proxy.newProxyInstance(
//1. 첫번째 실제로 가진녀석의 기능알 알려주어야해
DefaultCalculator.class.getClassLoader()
//2. 두번째 인터페이스 목록을 넣어주자!
//구현체가 구현하고있는 인터페이스목록이야
//여러개면 다 넣어주자!
, new Class[] {Calculator.class}
//3. 세번째 곁다리를 넣어주는거야!
//넣어주느 공간이야!
//람다로 바꾸어줬어
,(Object proxy, Method method, Object[] ags) -> {
// TODO Auto-generated method stub
//이렇게 앞뒤로 다 꽂아넣을 수있어
System.out.printf("호출되고있는 메소드 이름 %s\n", method.getName());
System.out.printf("%s 메소드 호출 전\n",method.getName());
Object result = method.invoke(진짜2, ags); //정적으로 calc호출하는것
System.out.printf("결과값 : %d \n ",result);
System.out.printf("%s 메소드 호출 후\n",method.getName());
return (int) result;
});
Calculator calc = 가짜;
int result = 0;
result = calc.plus();
System.out.printf("plus result : %d\n", result);
System.out.println();
result = calc.sub();
System.out.printf("sub result : %d\n", result);
System.out.println();
result = calc.multi();
System.out.printf("multi result : %d\n", result);
System.out.println();
}
}
calc가 진짜인지 가짜인지에 따라 출력되는게 달라
4)
원하는 함수만 log를 원하면 범주를 정해주어야해 point cut이라고 하는데 자바는 없지만 스프링은 있어
4. Point Cut(Weaving, JoinPoint)
(특정 함수만 전후처리 넣는것을 포인트컷 -> 모든함수 조인 / 위빙 특정)
joinpoin란 모든 메소드인데, 포인트컷의 후보로 볼 수있다
weaving이란 꿰매는것 처럼 원하는 방식으로 하는것이다.
진짜 객체의 모든 것을 대상(join point)으로하는데, 진짜 객체의 두개 메소드만 대상(Pointcuts)으로 하고싶어
Target은 joinponit(후보)가 3개야!
proxy를 통해서 CrossCuttingConcern을 꽂아 넣을 수있는게 3개야
우리가 joinPoint를 대상으로 꽂아넣는것을 weaving(뜨개질 의미) 이라고 해
즉 곁다리와 Joinpoint를 뜨개질 하는거야
join point중에 원하는것만 cut하는거야!
java는 프록시는 만들수 있지만 포인트컷은 우리가 직접해야해 (포인트컷으로 범주한정 : 인증이나 로그 남길 때)
그런데 스프링에는 있다. Aspect J에는 있다. 스프링에는 쓸만큼만 / Aspect J 는 쓸대없이 많아! 스프링이 적당히 좋아!
꽂아넣는게 중요한게 아니라 사전사후 능력을 가지고있어
코드실행 전에 전할내용, 후처리내용 공통분모가 있으면 AOP로 라이브러리 만들수있어
그런데 라이브러리나 플랫폼만들때 의미가있는데 응용프로그램만든자에게 의미가 없어
이 경우 맞춰 쓰는법에 집중하면좋아
5. Ioslation
(1) 들어가며
이거를 어노테이션으로 녹아낸거야
커넥션 객체를 가로채서 AOP방식으로 proxy에서 얘내들의 호출을 가로채서 쓰고있어!
중요한건 얘내들이 proxy에서 호출하고자 하는 함수를 여럿호출하는동안에
스프링은 @ 이걸로 원자성은 해결이 가능해
Atomicity, Isolation 까지가 우리가 통제하는상황이야
그외는 우리의 통제 외야
'durability(영속성)' 디스크에 저장한다면 저장되어야하는데, 저장되지않는 상황도 처리해야해! 어떤상황이냐하면 정전등인경우야!
이경우 UPS(무정전 전원시스템) 도 있다. 또한 log로 남긴것만 commit 안되면 전원들어왔을 때 수작업 해주면돼
프로그래머가 손댈수 없어
'Consistency(일관성)' 제약조건관련된거야! 계좌이체 했는데 업무적인거야, 300원없는계좌에 3000원이체를 했어! 전체업무와 일관적이지 못한 경우야! 올바르게 RoleBack만되면돼
Isolation(고립화) 스프링은 고립화을 위해서 전파옵션과 고립도옵션을 제공해
(2) Ioslation
DBMS마다 달라
하나의 스레드에서는 명령어가 하나씩 순차적으로 진행될거야
스레드가 굉장히 많이 사용돼
사용자 1이 트랙잭션 요청하잖아 두개 명령어 실행되잖아, 이 경우 사용자 2가 쳐들어 오면 스레드가 실행되잖아
그런데 스레드가 각자 데이터를 가지고하면 문제가없는데, 같은 데이터를 거의 '동시'에 서블릿이 들어와서 '같은 자원'을 한명은 update, 한명은 delete 하는경우 고립화가 필요해
즉 같은 자원에 동시에 들어오는 경우야!
1. 보충
(1) 분산형 다층 어플리케이션
하나의 어플리케이션이 되는 모듈들이 각각의 계층에 분산되어서 동작하는 형태를 의미하는것이다.
(2) invoke
리플렉션에서 class 의 정보를 불러오기 위한 것이다.
(3) proxy 패턴
What (정의) : 사전적으로 대리인을 의미하는데, 실존객체는 자신의 본래역할(core concern)에만 집중하고, 나머지 부가기능 (cross cutting concern)위임하는 것이다.
Why (존재이유) : SOLID 원칙 중 sigle resposibility priciple 을 위한것이다.
How (방법) : porxy로 구현하는것이다.
2. 회고
1)
트랜잭션에 대해서 체감할수 있어서 너무 좋았다.
그리고 실제로 어떻게 코드상으로 구현이 가능한지 드디어 알게되었다. 막연히 DB시절에 배웠던것과 많이 달라서 좋다
2)
개발자, 운영자에게 도움이 될 수있는것을 위해서 AOP가 탄생했다는것이 흥미로웠다.
'배움 __IL > TIL 1기' 카테고리의 다른 글
TIL : 69번째- 230315 [3-2-수] (0) | 2023.03.15 |
---|---|
TIL : 68번째- 230314 [3-2-화] (0) | 2023.03.14 |
TIL : 66번째- 230310 [3-1-금] (0) | 2023.03.10 |
TIL : 65번째- 230309 [3-1-목] (0) | 2023.03.09 |
TIL : 64번째- 230308 [3-1-수] (0) | 2023.03.08 |