var val = 10; // 전역 변수 num을 선언
function changeValue() {
console.log(val); // 10
val = 20; // 전역 변수 함수 내부에서 변경
}
changeValue();
console.log(val); // 20
var val = 10; // 전역 변수 num을 선언
function changeValue() {
console.log(val); // undefined
var val = 20; // 지역 변수 값 초기화
}
changeValue();
console.log(val); // 10
var의 호이스팅
let, const는 아니지만 var는 변수 선언 시 undefined로 정의함
*changeValue 함수 내에서 일어나는 일* == 호이스팅
var val; // undefined
console.log(log); // undefined
val = 20;
전역 스코프에서 var val = 10;가 선언되고 초기화됩니다. val 변수는 10의 값을 가집니다.
changeValue(); 함수가 호출됩니다.
changeValue 함수 내에서 console.log(val)이 실행될 때 val 변수가 호이스팅되고 함수 스코프 내에서 선언되었습니다. 그러나 아직 초기화되지 않았으므로 undefined가 출력됩니다.
마지막으로 함수 내에서 var val = 20;이 실행되고, val 변수는 20으로 초기화됩니다.
var val = 10; // 전역 변수 num을 선언
function changeValue() {
var val = 20; // 지역변수 초기화
console.log(val); // 20
}
changeValue();
console.log(val); // 10
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@100;300;400;500;700;900&display=swap');
body {
font-family: 'Noto Sans KR', sans-serif;
}
export/import로 js 파일에서 만든 변수나 자료를 다른 js 파일에서 사용하기
post.js
var apple = 10;
export default apple
- export default는 파일 맨마지막 한번만 사용 가능
App.vue
import apple from './post.js'
- apple 대신 다른 단어 사용 가능
- .js 생략 가능
post.js
var apple = 10;
var apple2 = 100;
export {apple, apple2}
- 변수 2개나 함수도 사용 가능
App.vue
import {apple, apple2} from './assets/post.js';
apple
apple2
사실은 스프링 입문은 8월에 완강을 하였다. 유료 강의를 구매한게 아마 수박씨 닷컴이 마지막이었는데 커리큘럼을 다 따라가면 스프링은 60만원대, JPA까지하면 일백만원이 훌쩍 넘어가는 금액이 나에게는 큰 지출이어서 꽤나 오랜 시간동안 고민을 해왔다. 그리고 나와 같은 고민을 하는 분들도 더러 많을 것 같다.
사람마다 다르겠지만 일단 무료 강의를 들으면 유료 강의를 살지 말지 바로 결정이 지어질거라고 생각된다. 개인 프로젝트를 하며 얻는 것도 귀중하지만 김영한님 강의를 통하여 쉽게 얻을 수 없는 것들을 많이 얻게 되고 또 그 지식의 단단한 힘과 신뢰성을 보고 믿고 따라 가게 된다. 이제 생각해보면 조금 더 빨리 수강할걸 이라는 짧은 후회가 있지만, 지금이라도 시작해서 다행이다. 내 개발 커리어에 유의미한 배움이 되길 원하며 하루를 더 나은 내일, 일주일로 만들어 나갈 것이다!
추석 할인 기간까지 꼬박 기다려 (사실 기본 원리 이후 지금까지 너무 정신이 없어서 강의를 구매해도 수강하지 못했을 것 같다.) 무려 25%나 할인받아 강의를 구매했다. 스프링 커리큘럼은 모두 구매하고 JPA 강의는 기본편만 일단 구매했다. (다 들으면 설날할인으로 구매 예정)
DataSource dataSource;
@Autowired
public SpringConfig(DataSource dataSource) {
this.dataSource = dataSource;
}
@Bean
public MemberRepository memberRepository(){
//new 키워드 뒤에 인터페이스 구현 클래스
//return new MemoryMemberRepository();
return new JdbcMemberRepostiory(dataSource);
}
스프링의 DI (Dependencies Injection)을 사용하면 기존 코드를 전혀 손대지 않고, 설정(어셈블리 라인)만으로 구현 클래스(구현체)를 변경할 수 있다.
>> 객체지향의 다형성을 활용
Service 단에서 어떤 Repository를 의존할지 기존 코드를 다 수정해야하지만 DI를 사용하면 SpringConfig만 손봐주면 된다는 말
이후에 JDBCTemplate , JPA, 스프링 데이터 JPA도 동일하게 구현체를 쉽게 변경할 수 있었다.
🎈🎈JDBCTemplate
DataSource dataSource;
@Autowired
public SpringConfig(DataSource dataSource) {
this.dataSource = dataSource;
}
@Bean
public MemberRepository memberRepository(){
//new 키워드 뒤에 인터페이스 구현 클래스
//return new MemoryMemberRepository();
//return new JdbcMemberRepostiory(dataSource);
return new JdbcTemplateMemberRepository(dataSource);
}
🎈🎈JPA
//@PersistenceContext
private EntityManager em;
@Autowired
public SpringConfig(EntityManager em) {
this.em = em;
}
@Bean
public MemberRepository memberRepository(){
//new 키워드 뒤에 인터페이스 구현 클래스
//return new MemoryMemberRepository();
//return new JdbcMemberRepostiory(dataSource);
//return new JdbcTemplateMemberRepository(dataSource);
return new JpaMemberRepository(em);
}
+) 엔티티 매핑 필요
@Entity
public class Member {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
}
*참고) build.gradle 수정, resources/application.properties 수정도 필요
🎈🎈스프링 데이터 JPA
: JpaRepository를 상속하는 인터페이스를 만들면 자동으로 구현체를 등록해준다. (Repository bean등록 안해도 됨)
@Configuration
public class SpringConfig {
private final MemberRepository memberRepository;
public SpringConfig(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Bean
public MemberService memberService() {
return new MemberService(memberRepository);
}
}
javax.persistence.PersistenceException: org.hibernate.id.IdentifierGenerationException: ids for this class must be manually assigned before calling save()
오류 : JPA를 사용할 때 PK인 컬럼을 도메인에 설정해주어야 함
해결 : @GeneratedValue 자동생성
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
: 스프링 실행 시 스프링 컨테이너라는 통이 생기는데 @Controller가 있으면 회원 컨트롤러 객체 생성하여 컨테이너에 넣어두고 스프링이 관리함. (스프링 Bean 생성)
@Controller
public class MemberController {
private MemberService memberService;
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
}
💥다음과 같이 객체 여러번 생성할 이유가 없다.
private final MemberService memberService = new MemberService();
🎈🎈DI (Dependency Injection), 의존성 주입 :객체 의존관계를 외부에서 넣어주는 것
생성자에 @Autowired 가 있으면 스프링이 연관된 객체를 스프링 컨테이너에서 찾아서 넣어준다.
(이전 테스트에서는 개발자가 직접 주입했고, 여기서는 @Autowired에 의해 스프링이 주입해준다.)
>> 회원 컨트롤러가 스프링 컨테이너에 생성 > 생성자를 호출 > 생성자에 @Autowired라고 되어있으면 스프링이 멤버 서비스를 스프링 컨테이너에 있는 멤버서비스에 연결시켜줌
🎈스프링 빈을 등록하는 2가지 방법
1. 컴포넌트 스캔(아래의 어노테이션은 컴포넌트가 근본)과 자동 의존관계 설정
실행 시, @Component어노테이션이 있으면 스프링 빈으로 자동 등록된다
@Controller : 외부 요청을 받음
@Service : 비지니스 로직을 만듦
@Repository : 데이터를 저장
@Autowired : 연관관계 설정
참고: 스프링은 스프링 컨테이너에 스프링 빈을 등록할 때, 기본으로 싱글톤으로 등록한다(유일하게 하나만등록해서 공유한다) 따라서 같은 스프링 빈이면 모두 같은 인스턴스다. 설정으로 싱글톤이 아니게 설정할 수 있지만, 특별한 경우를 제외하면 대부분 싱글톤을 사용한다.
ex) OrderService가 memberRepository를 @Autowired 했을 경우, memberService와 같은 인스턴스를 넣어줌. 메모리 절약!
2. 자바 코드로 직접 스프링 빈 등록하기
@Configuration
public class SpringConfig {
@Bean
public MemberService memberService(){
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository(){
//new 키워드 뒤에 인터페이스 구현 클래스
return new MemoryMemberRepository();
}
}
@Bean로 컨테이너에 스프링 빈 등록을 하고 @Autowired 대신 리턴 값의 매개변수를 연결
참고: XML로 설정하는 방식도 있지만 최근에는 잘 사용하지 않으므로 생략한다.
참고: DI에는 필드 주입, setter 주입, 생성자 주입 이렇게 3가지 방법이 있다. 의존관계가 실행중에동적으로 변하는 경우는 거의 없으므로 생성자 주입을 권장한다.
//필드 주입 - 주의! 스프링 실행할 때만 넣어주고 중간에 바꿀 수가 없다..! 권장X
@Autowired
private MemberService memberService;
//생성자 주입 - 조립 시점에 생성자로 조립해두고 변경될 수 없도록 막아버리기
private final MemberService memberService;
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
//setter 주입 - public하게 노출되어 있어야 함. 한번 세팅이 되면 바꿀 일이 없으니 위험.
// memeberService.setMemberService(); 코드 적어야함
private MemberService memberService;
@Autowired
public void setMemberService(MemberService memberService) {
this.memberService = memberService;
}
참고: 실무에서는 주로 정형화된 컨트롤러, 서비스, 리포지토리 같은 코드는 컴포넌트 스캔을 사용한다. 그리고 정형화 되지 않거나, 상황에 따라 구현 클래스를 변경해야 하면 설정을 통해 스프링 빈으로 등록한다.
주의: @Autowired 를 통한 DI는 helloController, memberService등과 같이 스프링이 관리하는 객체에서만 동작한다. 스프링 빈으로 등록하지 않고 내가 직접 생성한 객체에서는 동작하지 않는다.
*스프링 컨테이너, DI 관련된 자세한 내용은 스프링 핵심 원리 강의에서 설명한다.
면접에서 어노테이션에 대한 질문을 받은 적이 있다. 한번도 타인에게 설명해본 적이 없어서 당황하며 Controller, Service에 어노테이션을 붙여 경로를 찾게 도와준다고 답변했었다. Spring Bean를 덧붙여서 말하긴 했는데 얼추 맞긴 했다..
내가 아는건 딱 여기까지고(@Autowired를 활용해서 주입시켜주는 것까지?) 강의를 들으며 컴포넌트 스캔 원리를 알게 되어 띵~했다. 스프링을 배울 때 스프링 빈이라는 개념이 추상적이고 와닿지가 않았다. 왜냐하면 구글링할 때는 어노테이션 쓰는 방법과 직접 Config를 작성하는 방법 둘 다 있으니 제대로 이해를 못하고 기능 구현에 급급했던 것 같다. (분명,,스프링 부트의 장점이 직접 하나하나 설정안해도 된다는거였는데,,뭐지? 혼란에 빠졌었다.) 후후 스프링을 실행시킬 때 컨테이너가 있는데 객체 생성할 때 컴포넌트 스캔으로 컨트롤러를 찾는구나!
강의 들으면서 생성자 주입으로 실습을 하다보니 내가 이때까지 한건 뭐지..?라는 생각이 들었는데 DI에 대한 종류를 설명해주셔서 그저 감사했다. 나는 계속 필드 주입을 사용했구나(제일 안좋은듯;;) 이제부터 필드에 접근할 수 없고 조립 시 유용한 생성자 주입을 써야겠다.
아래는 작년에 DB 관련 히카리CP 설정을 할 때 쓴 Config 파일,,이땐 @Bean이 무슨 용도인지 잘몰랐다,,,
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
@Configuration
@PropertySource("classpath:/application.properties") //application.properties를 사용할 수 있도록 설정 파일의 위치를 정해 준다.
public class DatabaseConfiguration {
@Autowired
private ApplicationContext applicationContext; //bean 객체를 생성하고 관리하는 기능(beanfactory를 상속받음.)
//application.properties에 설정했던 데이터베이스 관련 정보를 사용하도록 지정한다.
//@ConfigurationProperties 어노테이션에 prefix가 spring.datasource.hikari로 설정되었기 때문에
//spring.datasource.hikari로 시작하는 설정을 이용해서 히카리CP의 설정파일을 만든다.
@Bean
@ConfigurationProperties(prefix="spring.datasource.hikari") //@ConfigurationProperties : *.properties, *.yml 파일에 있는 property를 자바 클래스에 값을 가져와서 사용 할 수 있게 해주는 어노테이션
public HikariConfig hikariConfig() {
return new HikariConfig();
}
//앞에서 만든 히카리CP의 설정파일을 이용해서 데이터베이스와 연결하는 데이터 소스를 생성한다.
//여기서는 데이터 소스가 정상적으로 생성되었는지 확인하기 위해서 데이터 소스를 출력했다.
@Bean
public DataSource dataSource() throws Exception {
DataSource dataSource = new HikariDataSource(hikariConfig());
System.out.println(dataSource.toString());
return dataSource;
}
@Bean
@ConfigurationProperties(prefix = "mybatis.configuration") //@ConfigurationProperties을 통해서 application.properties에서 prefix가 mybatis.configuration인 설정을 가져온다.
public org.apache.ibatis.session.Configuration mybatisConfig(){
return new org.apache.ibatis.session.Configuration(); //가져온 마이바티스 설정을 자바 클래스로 만들어 반환한다.
}
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { //SqlSessionFactory : mybatis와 mysql 서버를 연동시켜줌, sqlsession 생성
//sqlsession : 세션을 한번 생성하면 매핑구문을 실행하거나 커밋 또는 롤백을 하기 위해 세션을 사용할수 있다. 더 이상 필요하지 않은 상태가 되면 세션을 닫는다.
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
sqlSessionFactoryBean.setMapperLocations(applicationContext.getResources("classpath:/mapper/**/*.xml")); //** : 하위폴더전체
sqlSessionFactoryBean.setConfiguration(mybatisConfig()); //해당 설정을 sqlSessionFactory에 설정해준다.
return sqlSessionFactoryBean.getObject();
}
@Bean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { //SqlSessionTemplate은 SqlSession을 구현하고 코드에서 SqlSession를 대체하는 역할을 한다.
return new SqlSessionTemplate(sqlSessionFactory);
}
}
한 강의마다 시간은 짧은데 반복해서 듣다보니 시간이 꽤 걸린다. 다 들었을 땐 레벨업되어 있기를!!