spring_3일차_AOP_@

youuu·2022년 10월 19일
0

SPRING

목록 보기
3/33

@Configuration
: 설정파일을 만들기 위한 어노테이션 or Bean을 등록하기 위한 어노테이션.

@Bean
: 개발자가 직접 제어가 불가능한 라이브러리를 사용할때, 초기설정을 하기위해 사용.

@Component
: 개발자가 직접 개발한 클래스를 Bean으로 등록하고 싶을땐 해당 어노테이션을 사용.

📂 env03

📋 AdminConnection .java


package env03;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

public class AdminConnection implements DisposableBean, InitializingBean {
	private String adminId;
	private String adminPw;
	private String sub_adminId;
	private String sub_adminPw;
	
	public void afterPropertiesSet() throws Exception {
		System.out.println("AdminConnection afterPropertiesSet() 생성자 생성이후...");
	}

	public void destroy() throws Exception {
		System.out.println("AdminConnection destroy() 소멸자 소멸전...");
	}

	public String getAdminId() {
		return adminId;
	}

	public void setAdminId(String adminId) {
		this.adminId = adminId;
	}

	public String getAdminPw() {
		return adminPw;
	}

	public void setAdminPw(String adminPw) {
		this.adminPw = adminPw;
	}

	public String getSub_adminId() {
		return sub_adminId;
	}

	public void setSub_adminId(String sub_adminId) {
		this.sub_adminId = sub_adminId;
	}

	public String getSub_adminPw() {
		return sub_adminPw;
	}

	public void setSub_adminPw(String sub_adminPw) {
		this.sub_adminPw = sub_adminPw;
	}
}





📋 ApplicationConfig .java

@Configuration : 환경작업시 사용
@Bean : bean 설정

  • 환경작업 (생성자처럼 실행)
    admin3.properties, sub_admin3.properties를 읽음
  • [2] 배열을 두개로 잡고 [0], [1] 로 각각 세팅 => configurer에 넣어줌
  • 환경변수로 잡은것을 넣어줌
    adminConnection.setAdminId(adminId);
    adminConnection.setAdminPw(adminPw);
    adminConnection.setSub_adminId(sub_adminId);
    adminConnection.setSub_adminId(sub_adminSw);
  • @Bean 은 xml에서 <Bean> 한것과 같다.

package env03;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

@Configuration
public class ApplicationConfig {
	
	@Value("${admin.id}")
	private String adminId;
	@Value("${admin.pw}")
	private String adminPw;
	@Value("${sub_admin.id}")
	private String sub_adminId;
	@Value("${sub_admin.pw}")
	private String sub_adminPw;
	
	@Bean
	public static PropertySourcesPlaceholderConfigurer Properties( ) {
		PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
		System.out.println("2. Properties Run..");
		Resource[] locations = new Resource[2];
		locations[0] = new ClassPathResource("admin3.properties");
		locations[1] = new ClassPathResource("sub_admin3.properties");
		configurer.setLocations(locations);
		
		return configurer;
	}
	
	@Bean
	public AdminConnection adminConfig() {
		AdminConnection adminConnection = new AdminConnection();
		System.out.println("3. adminConfig Run..");
		adminConnection.setAdminId(adminId);
		adminConnection.setAdminPw(adminPw);
		adminConnection.setSub_adminId(sub_adminId);
		adminConnection.setSub_adminPw(sub_adminPw);
		
		return adminConnection;
	}
}





📋 EnvMainClass03 .java


package env03;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class EnvMainClass03 {

	public static void main(String[] args) {
		System.out.println("1. EnvMainClass03 Run...");
		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ApplicationConfig.class);
		System.out.println("4. EnvMainClass03 adminConfig Before Run...");
		AdminConnection connection = ctx.getBean("adminConfig", AdminConnection.class);
		System.out.println("5. EnvMainClass03 adminConfig After Run...");
		
		System.out.println("connection.getAdminId adminID : " + connection.getAdminId());
		System.out.println("connection.getAdminPw adminPW : " + connection.getAdminPw());
		System.out.println("connection.getSub_adminId sub_adminID : " + connection.getSub_adminId());
		System.out.println("connection.getSub_adminPw sub_adminPW : " + connection.getSub_adminPw());
		ctx.close();
	}

}

💻 결과화면 :

❗ 순서





📋 admin3 .properties


admin.id=super
admin.pw=12345





sub_admin3 .properties


sub_admin.id=sub3
sub_admin.pw=67890





📂 env04

📋 ServerInfo .java


package env04;

public class ServerInfo {
	private String ipNum;
	private String portNum;
	
	public String getIpNum() {
		return ipNum;
	}
	public void setIpNum(String ipNum) {
		this.ipNum = ipNum;
	}
	public String getPortNum() {
		return portNum;
	}
	public void setPortNum(String portNum) {
		this.portNum = portNum;
	}
}





📋 EnvMainClass04 .java


package env04;

import java.util.Scanner;
import org.springframework.context.support.GenericXmlApplicationContext;

public class EnvMainClass04 {

	public static void main(String[] args) {
		String config = null;
		System.out.println("System을 입력하세요? dev OR run");
		Scanner scanner = new Scanner(System.in);
		String str = scanner.next();
		if(str.equals("dev")) {
			config = "dev";
		} else if(str.equals("run")) {
			config = "run";
		}
		scanner.close();
		
		GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
        ctx.getEnvironment().setActiveProfiles(config);
		ctx.load("applicationCTX_dev.xml", "applicationCTX_run.xml");
		
		ctx.refresh();
		
		ServerInfo info = ctx.getBean("serverInfo",ServerInfo.class);
		System.out.println("ip : " + info.getIpNum());
		System.out.println("port : " + info.getPortNum());
		ctx.close();
	}
}

💻 결과화면 :





📋 applicationCTX_dev .xml

  • profile="dev" dev로 지정.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" 
	profile="dev">
	
	<bean id="serverInfo" class="env04.ServerInfo">
		<property name="ipNum" 	 value="localhost"></property>
		<property name="portNum" value="8181"></property>
	</bean>
</beans>





📋 applicationCTX_run .xml


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" 
	profile="run">

	<bean id="serverInfo" class="env04.ServerInfo">
		<property name="ipNum" value="172.30.1.98"></property>
		<property name="portNum" value="8080"></property>
	</bean>

</beans>





⭐ 주요개념

📕 Spring 프레임워크

  1. CORE: IoC, DI, DDD를 기반으로하는 디자인 패턴
  2. MVC : 웹어플리케이션 제작을 위한 기반제공
  3. AOP : 프록시기반의 AOP 기반 인프라 제공
  4. ORM : Hibernate, iBatis의3rdParty 플러그인 제공
  5. DAO: 데이터를 액세스하기 위한 기반제공

📕 AOP

: Aspect Oriented Programming

1. 기능 외적인 관점의 용이한 적용을 위한 패러다임. AOP의 개념

1) 관점 지향 프로그램(Aspect Oriented Programming, AOP)의 정의

  • 핵심 관심사(Core Concerns)에 대한 관점과 횡단 관심사(Cross-cutting
    Concerns)에 대한 관점들로 프로그램을 분해해 객체지향 방식(OOP)에서
    추구하는 모듈을 효과적으로 지원하도록 하는 프로그래밍 기법.
    2) AOP의 등장배경

2. AOP의 등장배경

  1. 기존 OOP 한계
  • 하나의 클래스에 핵심과 횡단 관심사가 혼재되어 프로그램의 가독성 및 재활용성에 비효율이 발생, 관심사를 분리/단순화 하여 코드의 재활용과 유지
    보수의 효율성을 극대화 .
  1. AOP의 시스템 적용 예시
  • OOP의 구조는 계좌이체 클래스, 입출금 클래스, 이자계산 클래스로 나뉘게 되고, 로깅, 보안, 트랜잭션 기능이 분산해서 존재
  • 타 프로젝트에서 기 구성 시스템 중 보안기능만을 분리한다고 할 경우 기존의 OOP방식으로는 이 보안 역할만을 별도 분리 불가.

3. AOP의 특징

4. AOP의 개념도 및 주요 요소

  • Join Point : 횡단 관심의 기능이 삽입되어 실행될 수 있는 프로그램 내의 실행될 위치 혹은 호출 Event

1) AOP의 개념도

⭐⭐⭐ 개념

2) AOP의 주요 요소

  • Core Concern 핵심관심
    Business 업무. 핵심 기능, 가치

  • Cross-cutting 횡단관심
    부가적인 요구사항. 보안, 인증, 로그작성, 정책적용 등

  • Aspect
    흩어진 관심사(Crosscutting Concerns)를 묶어서 모듈화 한 것.
    하나의 모듈. AdvicePoint Cut이 들어간다.
    특정 관심사에 관련된 코드만을 캡슐화

  • Target
    Aspect가 가지고 있는 Advice가 적용되는 대상(클래스, 메서드 등등)을 말한다.

  • Advice
    어떤 일을 해야할 지에 대한 것. 해야할 일들에 대한 정보를 가지고 있다.

  • Join Point
    가장 흔한 Join Point는 메서드 실행 시점.
    Advice가 적용될 위치, 끼어들 수 있는 지점. 생성자 호출 직전, 생성자 호출 시, 필드에 접근하기 전, 필드에서 값을 가져갔을 때 등등.

  • Point Cut
    Join Point의 상세한 스펙을 정의한 것.
    어디에 적용해야 하는지에 대한 정보를 가지고 있다. “A 클래스에 B 메서드를 적용할 때 호출을 해라.”와 같은 구체적인 정보를 준다.

  • Weaving
    Join Point에 해당하는 Advice를 삽입하는 과정.
    Aspect와 핵심 관심사를 엮는 것(weave)



4. Spring AOP의 특징


⭐⭐⭐ 개념

5. Spring AOP Advice 종류(*)

  • Around 가 70%정도 사용한다.⭐⭐⭐
    ✔ 실행 전, 후, 예외 발생 시점에 모두 실행해야 할 로직을 담기 때문.

Before, After Returning, After Throwing, After, Around
xml 스키마기반, @Aspect 애노테이션 기반 으로 사용 할 수 있다.






💼 och06_AOP1

📂 aop1



📋 student .java


package aop1;

public class Student {
	private String	name;
	private int		age;
	private int 	gradeNum;
	private int 	classNum;
	
	public void getStudentInfo() {
		System.out.println("이름 : " + getName());
		System.out.println("나이 : " + getAge());
		System.out.println("학년 : " + getGradeNum());
		System.out.println("반 : "  + getClassNum());
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public int getGradeNum() {
		return gradeNum;
	}

	public void setGradeNum(int gradeNum) {
		this.gradeNum = gradeNum;
	}

	public int getClassNum() {
		return classNum;
	}

	public void setClassNum(int classNum) {
		this.classNum = classNum;
	}
}



📋 student .java


package aop1;

public class Worker {
	private String	name;
	private int		age;
	private String	job;
	
	public void getWorkerInfo() {
		System.out.println("이름 : " + getName());
		System.out.println("나이 : " + getAge());
		System.out.println("직업 : " + getJob());
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public String getJob() {
		return job;
	}

	public void setJob(String job) {
		this.job = job;
	}
}



📋 LogAop .java

  • 성능측정
  • 핵심 관심사에 들어가는 지점 joinpoint

package aop1;

import org.aspectj.lang.ProceedingJoinPoint;

public class LogAop {

	// Around Advice에서 사용할 공통기능 메서드는,대부분 파라미터로 전달받은  
	// ProceedingJoinPoint의 proceed() 메서드만 호출
	public Object loggerAop(ProceedingJoinPoint joinPoint) throws Throwable {
		
		// 핵심 관심사 Method
		String signatureStr = joinPoint.getSignature().toShortString();
		System.out.println(signatureStr + "is start..");
		// Returns the current time in Millis
		long startTime = System.currentTimeMillis();
		
		Object obj;
		try {
			// 핵심 관심사 Method 수행
			obj = joinPoint.proceed();
			return obj;
		} finally {
			long endTime = System.currentTimeMillis();
			System.out.println(signatureStr + "is finished.");
			System.out.println(signatureStr + "경과시간 : " + (endTime - startTime));
		}

	}
}



📍 AopMainClass01 .java


package aop1;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

public class AopMainClass01 {

	public static void main(String[] args) {
		AbstractApplicationContext ctx = new GenericXmlApplicationContext("classpath:applicationCTX01.xml");
		
		Student student = ctx.getBean("student", Student.class);
		student.getStudentInfo();
		
		Worker worker = ctx.getBean("worker", Worker.class);
		worker.getWorkerInfo();
		ctx.close();
	}
}

💻 결과화면 :



📋 applicationCTX01 .xml 만들기

aop, beans를 찍고 생성



📋 applicationCTX01 .xml

  • aspect : 특정 상황(point-cut)과 그 상황에서 수행할 작업 (advice)의 집합
    🔵 특정 상황, 수행할 작업
  • pointcut : 관심사를 구현한 코드에 끼워 넣을수 있는 프로그램의 Event
  • around : target 객체의 메소드 실행 전, 후 또는 예외 발생 시점에 모두 실행해야 할 로직을 담아
    야 할 경우
    🔵 시작과 끝 에 실행할 로직 (before, after 둘다 쓴다)

<aop:pointcut expression="within(aop1.S*)" id="pointcut1"/> 로 변경하면


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
	
	<bean id="logAop" class="aop1.LogAop"></bean>
	

	<aop:config>
		<aop:aspect id="logger1" ref="logAop">
			<aop:pointcut expression="within(aop1.*)" id="pointcut1"/>
			<aop:around method="loggerAop" pointcut-ref="pointcut1"/>
		</aop:aspect>
	</aop:config>
	
	<bean name="student" class="aop1.Student">
		<property name="name"		value="연개소문"></property>
		<property name="age" 		value="50"/>
		<property name="gradeNum"	value="3"/>
		<property name="classNum"	value="5"/>
	</bean>
	
	<bean name="worker" class="aop1.Worker">
		<property name="name"	value="이순신"/>
		<property name="age" 	value="35"/>
		<property name="job" 	value="개발자"/>
	</bean>
	
</beans>





💼 och06_AOP2

och06_AOP2 | aop2 | aop2.buz(비즈니스) |

📂 aop2.buz



📋 student .java

aop1 student.java 복붙 + 어쩌다 오류 부분 추가

package aop2.buz;

public class Student {
	private String	name;
	private int		age;
	private int 	gradeNum;
	private int 	classNum;
	
	public Student() {
		System.out.println("Student 생성자");
	}
	
	public void getStudentInfo() {
		System.out.println("이름 : " + getName());
		System.out.println("나이 : " + getAge());
		System.out.println("학년 : " + getGradeNum());
		System.out.println("반 : "  + getClassNum());
		/*
		 * // 어쩌다 오류 System.out.println(10/0);
		 */
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public int getGradeNum() {
		return gradeNum;
	}

	public void setGradeNum(int gradeNum) {
		this.gradeNum = gradeNum;
	}

	public int getClassNum() {
		return classNum;
	}

	public void setClassNum(int classNum) {
		this.classNum = classNum;
	}
}



📋 Worker .java

aop1 Worker.java 그대로 복붙



📂 aop2

📋 LogAop .java


package aop2;

import org.aspectj.lang.ProceedingJoinPoint;

public class LogAop {
	public Object loggerAop(ProceedingJoinPoint joinPoint) throws Throwable {
		
		//  핵심 업무에 사용 method
		String signatureStr = joinPoint.getSignature().toShortString();
		long st = System.currentTimeMillis();
		System.out.println(signatureStr + "is start..");
		
		try {
			// 핵심업무 수행 aop2.buz.Stuent.getStudentInfo()
			Object obj = joinPoint.proceed();
			return obj;
		} finally {
			long et = System.currentTimeMillis();
			System.out.println(signatureStr + "is finished.");
			System.out.println(signatureStr + "경과시간 : " + (et - st));
		}
	}
	
	public void beforeAdvice() {
		System.out.println("beforeAdvice()");
	}
	
	public void afterReturningAdvice() {
		System.out.println("afterReturningAdvice()");
	}
	
	public void afterThrowingAdvice() {
		System.out.println("afterThrowingAdvice()");
	}
	
	public void afterAdvice() {
		System.out.println("afterAdvice()");
	}

}



📍 AopMainClass02 .java


package aop2;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

import aop2.buz.Student;
import aop2.buz.Worker;

public class AopMainClass02 {

	public static void main(String[] args) {
		AbstractApplicationContext ctx = new GenericXmlApplicationContext("classpath:applicationCTX02.xml");
		
		Student student = ctx.getBean("student", Student.class);
		student.getStudentInfo();

		Worker worker = ctx.getBean("worker", Worker.class);
		worker.getWorkerInfo();
		
		ctx.close();
	}
}

💻 결과화면 :



📋 applicationCTX02 .xml


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">

	<bean id="logAop" class="aop2.LogAop"></bean>
		<aop:config>
			<aop:aspect id="logger" ref="logAop">
				<aop:pointcut expression="within(aop2.buz.*)" id="pointcut1"/>
				<aop:around method="loggerAop" pointcut-ref="pointcut1"/>
			</aop:aspect>
			
			<aop:aspect id="logger" ref="logAop">
				<aop:pointcut expression="within(aop2.buz.*)" id="pointcut2"/>
				<aop:before method="beforeAdvice" pointcut-ref="pointcut2"/>
			</aop:aspect>	
				
			<aop:aspect id="logger" ref="logAop">
				<aop:pointcut expression="within(aop2.buz.*)" id="pointcut3"/>
				<aop:after-throwing method="afterThrowingAdvice" pointcut-ref="pointcut3"/> 
			</aop:aspect>	
			 
			<!-- <aop:aspect id="logger" ref="logAop">
				<aop:pointcut expression="within(aop2.buz.*)" id="pointcut4"/>
				<aop:after-returning method="afterReturningAdvice" pointcut-ref="pointcut4"/>
			</aop:aspect>	 -->
				
			<aop:aspect id="logger" ref="logAop">
				<aop:pointcut expression="within(aop2.buz.*)" id="pointcut4"/>
				<aop:after method="afterAdvice" pointcut-ref="pointcut4"/> 
			</aop:aspect>		
		</aop:config>
	
	<bean id="student" class="aop2.buz.Student">
		<property name="name"	  value="김춘추"></property>
		<property name="age"      value="10"></property>
		<property name="gradeNum" value="3"></property>
		<property name="classNum" value="5"></property>
	</bean>
	
	<bean id="worker" class="aop2.buz.Worker">
		<property name="name" value="김유신"></property>
		<property name="age"  value="35"></property>
		<property name="job"  value="개발자"></property>
	</bean>

</beans>



💼 och06_AOP3

och06_AOP3 - aop3 , aop3.buz

📂 aop3.buz

📋 Student .java


package aop3.buz;

public class Student {
	private String	name;
	private int		age;
	private int 	gradeNum;
	private int 	classNum;
	
	public void getStudentInfo() {
		System.out.println("이름 : " + getName());
		System.out.println("나이 : " + getAge());
		System.out.println("학년 : " + getGradeNum());
		System.out.println("반 : "  + getClassNum());
	}
	
	public void get3StudentInfo() {
		System.out.println("이름 3 : " + getName());
		System.out.println("나이 3 : " + getAge());
		System.out.println("학년 3 : " + getGradeNum());
		System.out.println("반 3 : "  + getClassNum());
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public int getGradeNum() {
		return gradeNum;
	}

	public void setGradeNum(int gradeNum) {
		this.gradeNum = gradeNum;
	}

	public int getClassNum() {
		return classNum;
	}

	public void setClassNum(int classNum) {
		this.classNum = classNum;
	}
}

📋 Worker .java

이전꺼 복붙해서 사용



📋 LogAop .java

📌 같은것 다른방법으로

@Aspect, @Around, @Before, @After
02장에서 한 <aop:after>, <aop:aspect>, <aop:around>, <aop:before> 와 같다.


package aop3;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class LogAop {
	
	// aop3.buz 패키지 안에 있는 모든 메소드
	@Pointcut("within(aop3.buz.*)")
	private void pointcutMethod() {
	}
	
	@Around("pointcutMethod()")
	public Object loggerAop(ProceedingJoinPoint joinPoint) throws Throwable {
		String signatureStr = joinPoint.getSignature().toShortString();
		System.out.println(signatureStr + " is start.");
		long st = System.currentTimeMillis();
		
		try {
			Object obj = joinPoint.proceed();
			return obj;
		} finally {
			long et = System.currentTimeMillis();
			System.out.println(signatureStr + " is finished.");
			System.out.println(signatureStr + " 경과시간 : " + (et - st));
		}
	}
	
	@Before("within(aop3.buz.*)")
	public void beforeAdvice() {
		System.out.println("beforeAdvice()");
	}
	
	@After("within(aop3.buz.*)")
	public void afterAdvice() {
		System.out.println("afterAdvice()");
	}
}



📍 AopMainClass03 .java


package aop3;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

import aop3.buz.Student;

public class AopMainClass03 {

	public static void main(String[] args) {
		AbstractApplicationContext ctx = new GenericXmlApplicationContext("classpath:applicationCTX03.xml");
		
		Student student = ctx.getBean("student", Student.class);
		student.getStudentInfo();
		student.getStudentInfo();
		student.get3StudentInfo();

		
		ctx.close();
	}
}

💻 결과화면 :


profile
공부중인 주니어 개발자

0개의 댓글