2021년 9월 21일 평택에 있는 지제를 시작으로 고덕 신도시까지 확인하는 임장을 다녀왔다.
이번 임장 포인트는 이렇게 확인할 수 있다.
1. 고덕 신도시 확인
2. 평택 삼성 반도체 확인
3. 지제역을 중심으로 교통 환경 확인
4. 브레인시티 부지 확인
1. 고덕 신도시는 신도시 답게 아파트들이 정갈(?)하게 들어서 있는 모습을 볼 수있었다.
호갱노노
국제 신도시에서 대장 아파트를 확인 해 보았다.
고덕 집값은 이제 막을 수 없다....
2. 삼성 반도체 2라인까지 현재 만들어져 라인이 가동 되고 있었다. 이번에 만들어지는 공장은 D램, 낸드플레시 뿐만 아니라 초 미세 파운더리와 메모리 반도체 생산을 담당하는 복합 반도체 공장이다.
한 라인에 약 만명의 삼성 직원이 필요하다고 한다.
뉴스 헤드라인
그리고 삼성 직원 외에 협력사와 건설업체 등 추가적인 간접 고용을 포함한다면 라인당 3~4만명의 고용 창출이 이루어진다. 역시 삼성 제국 답다. 삼성이 들어오는 곳에 왜 집값이 요동이 치는지 알 수 있는 시간이었다. 직접 눈으로 보니 어마어마 하다. 30년에 6라인까지 완공되고 나서의 모습을 생각하니 어질어질하다.
3. 삼성 근처에는 차가 막힐 수 밖에 없다 항상 지금까지 다른 지역 (기흥, 동탄)을 보면 알 수 있다. 평택 공장 주변으로 4차선의 도로가 있으나 출퇴근 시간에 막힐 것으로 예상된다. 실제로 임장 다닐땐 무리가 없는것으로 보아 평소에는 괜찮을것 같다. 워낙 도로가 잘 되어있었다.
4. 브레인 시티는 아직 개발 되어있는 진척도를 확인할 수 없었다. 다만 브레인 시티 예정 구역에 펜스가 쳐져 있었고 현수막이 걸려있었다. 하지만 관심이 없는 지역 주민들은 어떤것인지 잘 모르는 눈치였다.
=> 잠실=롯데 이듯이 평택=삼성 공식이 생길 날이 머지 않았다. 삼성이 가지고오는 영향력을 직접적으로 확인해 본 좋은 시간이었다.
Spring을 사용하면서 DB연동 할 일은 매우 많다. 그리고 DB연동에는 여러가지 과정을 거쳐야 하는데 Connection을 얻고 쿼리문을 날리고 결과를 리턴해야한다. 이러한 과정은 매우 번거롭다. 그래서 Spring에서 제공하는 JdbcTemplate을 이용하면 매우 쉽게 DB연동 작업을 할 수 있다.
이와같은 XML설정을 추가하면 된다. 이 설정 하나로 매번 Connection을 얻어올 필요가 없어졌다. JdbcTemplate은 Connection을 얻어올 때 위의 정보에 따라 얻어올 것이다.
그러면 입력과 출력은 어떻게 하는가?
입, 출력에 사용되는 대표적인 메서드가 몇개 있다.
update : 대표적으로 사용된다.
queryForInt : 출력이 int형으로 나올때(insert, delete, update) 사용하게 된다.
queryForObject : 출력이 객체 타입으로 나올때(select) select 결과는 주로 여러개의 컬럼 값이 나오게 된다. 하지만 이는 단 한개의 즉, 단 한줄의 행만 가지는 결과값이어야한다. ex) select count(*) from world; 이런식으로 말이다. 그렇다면 여러개가 나오는 경우는 어떻게 할까
query : 이렇게 사용하면 된다. ex)select * from world; 여러개의 컬럼이 여러개의 결과(행)을 가진 형태에 사용된다.
그렇다면 여러개의 객체를 리턴 했을 때 어떻게 받는것이 효과적인가?
바로 RowMapper를 사용하는것이 좋다. 이렇게 말이다.
package tommy.spring.web.board.impl;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.RowMapper;
import tommy.spring.web.board.BoardVO;
public class BoardRowMapper implements RowMapper<BoardVO> {
@Override
public BoardVO mapRow(ResultSet rs, int rowNum) throws SQLException {
BoardVO board = new BoardVO();
board.setSeq(rs.getInt("seq"));
board.setTitle(rs.getString("title"));
board.setWriter(rs.getString("writer"));
board.setContent(rs.getString("content"));
board.setRegDate(rs.getDate("regDate"));
board.setCnt(rs.getInt("cnt"));
return board;
}
}
이런 클래스를 만들고 이는 리턴된 객체가 VO형태로 매핑되어 객체가 리턴되는 클래스이다.
public BoardVO getBoard(BoardVO vo) {
System.out.println("Spring JDBC로 getBoard() 기능 처리");
Object[] args = {vo.getSeq()};
return jdbcTemplate.queryForObject(BOARD_GET, args, new BoardRowMapper());
}
public List<BoardVO> getBoardList(BoardVO vo) {
System.out.println("Spring JDBC로 getBoardList() 기능 처리");
return jdbcTemplate.query(BOARD_LIST, new BoardRowMapper());
}
위 그림을 보면 일반적으로 드는 예로 은행구조이다. 우린 은행에서 업무를 본다 주로 계좌이체나 대출 이런것들이 있을것이다. 이런 기능들은 프로그래밍 입장에서 핵심 기능들이다. 그리고 이러한 기능들을 하는데 무조건 실행하는 공통적인 기능들이 있다 바로 기록을 남기는 로깅이나 트랜젝션 등 이 있다 이런 것들을 횡단관심 (부가 기능)이라 한다. 이것을 왜 분리해야하는지 의문이들것이다.
객체 지향 관점에서 먼저 작성 해 보자. 코드 양이 좀 많다.
먼저 모든 메서드가 실행되기 전에 실행 될 코드를 작성해보자
package com.springbook.biz.common;
public class LogAdvice {
public void printLog() {
System.out.println("[공통 로그] 비즈니스 로직 수행 전 동작");
}
}
해당 코드는 모든 메서드가 실행 되기 전에 실행 될 것이다. 그러려면 service에서 객체를 생성해서 매번 호출 해야한다.
package com.springbook.biz.board.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.springbook.biz.board.BoardService;
import com.springbook.biz.board.BoardVO;
import com.springbook.biz.common.LogAdvice;
@Service("boardService")
public class BoardServiceImpl implements BoardService {
@Autowired
private BoardDAO boardDAO;
private LogAdvice log;
public BoardServiceImpl() {
}
@Override
public void insertBoard(BoardVO vo) {
log.printLog();
boardDAO.insertBoard(vo);
}
@Override
public void updateBoard(BoardVO vo) {
log.printLog();
boardDAO.updateBoard(vo);
}
@Override
public void deleteBoard(BoardVO vo) {
log.printLog();
boardDAO.deleteBoard(vo);
}
@Override
public BoardVO getBoard(BoardVO vo) {
log.printLog();
return boardDAO.getBoard(vo);
}
@Override
public List<BoardVO> getBoardList(BoardVO vo) {
log.printLog();
return boardDAO.getBoardList(vo);
}
}
이렇게 말이다. 이건 문제가 크게 되지 않는다. 다만 호출해야 하는 메서드랑 클래스가 달라진다면?? 코드를 수정해야한다.
package com.springbook.biz.common;
public class Log4jAdvice {
public void printLogging() {
System.out.println("[공통 로그-Log4j] 비즈니스 로직 수행 전 동작");
}
}
package com.springbook.biz.board.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.springbook.biz.board.BoardService;
import com.springbook.biz.board.BoardVO;
import com.springbook.biz.common.Log4jAdvice;
@Service("boardService")
public class BoardServiceImpl implements BoardService {
@Autowired
private BoardDAO boardDAO;
private Log4jAdvice log;
public BoardServiceImpl() {
}
@Override
public void insertBoard(BoardVO vo) {
log.printLogging();
boardDAO.insertBoard(vo);
}
@Override
public void updateBoard(BoardVO vo) {
log.printLogging();
boardDAO.updateBoard(vo);
}
@Override
public void deleteBoard(BoardVO vo) {
log.printLogging();
boardDAO.deleteBoard(vo);
}
@Override
public BoardVO getBoard(BoardVO vo) {
log.printLogging();
return boardDAO.getBoard(vo);
}
@Override
public List<BoardVO> getBoardList(BoardVO vo) {
log.printLogging();
return boardDAO.getBoardList(vo);
}
}
이처럼 말이다.
이제 AOP기능을 추가해보자 먼저 boardServiceImpl 클래스를 원상태로 돌리고 applicationContext.xml에 가서 추가해준다.
<bean id="log" class="com.springbook.biz.common.LogAdvice" /> <!-- 먼저 횡단관심에 해당하는 객체 생성 id는 aop:aspect ref와 매칭된다.-->
<aop:config> <!-- AOP 설정에 해당하는 요소 -->
<aop:pointcut expression="execution(* com.springbook.biz..*Impl.*(..))" id="allPointcut"/> <!-- id는 pointcut-ref와 매칭된다. -->
<aop:aspect ref="log">
<aop:before method="printLog" pointcut-ref="allPointcut"/>
</aop:aspect>
</aop:config>
그리고 실행시켜주면 아래 결과처럼 모든 메서드 앞에 실행 된것을 볼 수 있다.
package com.springbook.biz.board;
import java.util.List;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
public class BoardServiceClient {
public static void main(String[] args) {
AbstractApplicationContext container = new GenericXmlApplicationContext("applicationContext.xml");
BoardService boardService = (BoardService)container.getBean("boardService");
BoardVO vo = new BoardVO();
vo.setTitle("임시제목");
vo.setWriter("홍길동");
vo.setContent("일빠...");
boardService.insertBoard(vo);
List<BoardVO> boardList = boardService.getBoardList(vo);
for(BoardVO board : boardList) {
System.out.println("---> "+board.toString());
}
container.close();
}
}
위 xml 설정에서 알 수 없는 단어들이 많았을 것이다. AOP를 하면서 반드시 알아햐 하는 단어들이다.
joinPoint : 조인 포인트는 직역하면 만나는 지점이다. 따라서 횡단 관심에 해당하는 메서드가 핵심기능을 만나는 메서드를 만나는 지점을 의미한다. 여기선 serviceImpl의 모든 메서드가 해당이 된다.
pointCut : 포인트 컷은 직역하면 포인트를 자르다 라고 해석할 수 있는데이러한 조인포인트들 중에서 횡단관심이 실행 될 메서드 만을 잘라서 실행하는것을 의미한다.
Advice : 횡단 관심으로 여기선 LogAdvice와 Log4jAdvice가 해당이 된다.
Aspect : Aspect는 pointcut과 Advice의 결합으로 어떤 포인트 컷에 어떤 어드바이스가 결합하는지를 설정하는 것이다.
Weaving : 위빙은 핵심관심이 호출될 때 횡단관심이 삽입되는것을 의미하며 Aspect가 설정한 곳으로 위빙된다.
종합하면 위 설정중 aop:aspect라는 요소를 봤을 것이다.
com.springbook.biz..*Impl.*(..) -> 모든 return형을 가진 com.springbook.biz 패키지에 끝자리가 Impl을 가진 모든 클래스의 모든 메서드의 0개 이상의 모든 파라미터를 가진 joinpoint 중 log라는 아이디를 가진 클래스의 printLog메서드 Advice 가 allPointcut이라는 아이디를 가진 포인트 컷에 위빙된다고 할 수 있다.
이렇게 설정한것이라고 할 수 있다. 이렇게 AOP를 이용하면 유지보수도 훨씬 쉬워질 것이다.
이렇게 xml 설정 뿐만 아니라 어노테이션으로도 할 수 있다.
xml에 방금 aop설정을 주석처리하고 아래 문장을 추가한다.
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
package com.springbook.biz.common;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Service;
@Service
@Aspect
public class LogAdvice {
@Pointcut("execution(* com.springbook.biz..*Impl.*(..))")
public void allPointcut() {}
@Before("allPointcut()")
public void printLog() {
System.out.println("[공통 로그] 비즈니스 로직 수행 전 동작");
}
}
이후 LogAdvice를 이렇게 수정하는데 여기서 어노테이션이 좀 많이 보일 것이다.
1. @Pointcut : 포인트 컷은 횡단 관심이 실행될 메서드의 범위를 괄호 안에 지정하고 메서드로 만들었다. 이때 메서드 명은 xml에서 id가 된다.
2. @Before : 횡단 관심이 위빙 될 위치를 지정하는 어노테이션으로 괄호 안에 Pointcut의 메서드를 지정하여 메서드의 Pointcut 어노테이션의 포인트컷이 된 메서드가 실행 될때 실행된다. (말이 좀 어렵다.....)
* 이것말고 @After(무조건 실행), @AfterTrowing(예외 발생시 실행), @AfterReturning(결과가 리턴 될 경우 실행), @Around(전후로 실행) 이 있다. xml 설정처럼 사용하면 된다.
3. @Aspect : Advice와 Pointcut의 합친 용어로 해당 클래스에 Pointcut과 Advice가 있다는 것을 알려주는 어노테이션
4. @Service : 해당 메서드가 실행되려면 클래스의 객체가 있어야 하므로 사용된다.
1.@Component : 해당 클래스의 객체를 생성한다. @Component(“id”) id값값을 작성하여 해당 객체를 요청할 수 있게 한다. 작성하지 않는다면 해당 클래스 명의 맨 앞자리가 소문자로 변환되어 아이디가 자동으로 지정된다.
package polymorphism;
import org.springframework.stereotype.Component;
@Component("tv") // 이 tv아이디는
public class LgTV implements TV {
@Override
public void powerOn() {
System.out.println("LgTV 전원 켜짐");
}
@Override
public void powerOff() {
System.out.println("LgTV 전원 꺼짐");
}
@Override
public void volumeUp() {
System.out.println("LgTV 소리 커짐");
}
@Override
public void volumeDown() {
System.out.println("LgTV 소리 작아짐");
}
}
package polymorphism;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
public class TVUser {
public static void main(String[] args) {
AbstractApplicationContext factory = new GenericXmlApplicationContext("applicationContext.xml");
TV tv = (TV)factory.getBean("tv"); // @Component 어노테이션의 id와 getBean()의 아규먼트가 일치한다.
tv.powerOn();
tv.volumeUp();
tv.volumeDown();
tv.powerOff();
}
}
2. @Autowired : Autowired 어노테이션은 변수나 메서드 위에 작성되고 Autowired가 붙은 변수는 자료형을 확인하고 현재 메모리에 있는 (생성된 객체) 자료형과 일치하는 객체에 주입한다. 반드시 객체가 먼저 생성되어있어야 한다. 또한 여러개의 자료형이 동시에 존재할 수 있는데 그 문제는 아래 어노테이션을 사용해서 해결할 수 있다.
package polymorphism;
public class SamsungTV implements TV {
@Autowired
private Speaker speaker;
private int price;
public SamsungTV(Speaker speaker, int price) {
System.out.println("==> SamsungTV 다중 생성자 인젝션");
this.speaker = speaker;
this.price = price;
}
public Speaker getSpeaker() {
return speaker;
}
public void setSpeaker(Speaker speaker) {
System.out.println("setSpeaker()호출");
this.speaker = speaker;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
System.out.println("setPrice()호출");
this.price = price;
}
public SamsungTV() {
System.out.println("SamsungTV 객체 생성");
}
public SamsungTV(Speaker speaker) {
System.out.println("==> SamsungTV 생성자 인젝션");
this.speaker = speaker;
}
@Override
public void powerOn() {
System.out.println("SamsungTV 전원 켜짐"+", 가격 : "+price);
}
@Override
public void powerOff() {
System.out.println("SamsungTV 전원 꺼짐");
}
@Override
public void volumeUp() {
speaker.volumeUp();
}
@Override
public void volumeDown() {
speaker.volumeDown();
}
}
package polymorphism;
import org.springframework.stereotype.Component;
@Component("sony")
public class SonySpeaker implements Speaker{
public SonySpeaker() {
System.out.println("SonySpeaker 객체 생성");
}
public void volumeUp() {
System.out.println("SonySpeaker 소리 올린다.");
}
public void volumeDown() {
System.out.println("SonySpeaker 소리 내린다.");
}
}
우리는 분명 저 스피커가 sony스피커라고 한 적이 없다 심지어 speaker는 인터페이스다. 하지만 자동으로 Autowired 어노테이션으로 자동으로 변수에 객체가 주입된것을 볼 수 있다.
3. @Qualifier : @Autowired 어노테이션으로 객체를 주입하면 해당자료형을 가지는 객체는 유일해야한다. 하지만 그럴일이 없다. 따라서 우리가 정확히 객체를 지정해야하는데 이때 사용하는것이 Qualifier어노테이션이다. 우리는 Component어노테이션으로 객체 생성시에 id값을 지정했다. 이것을 여기에 사용하게 된다.
package polymorphism;
import org.springframework.stereotype.Component;
@Component("apple")
public class AppleSpeaker implements Speaker {
public AppleSpeaker() {
System.out.println("AppleSpeaker 객체 생성");
}
@Override
public void volumeUp() {
System.out.println("===> AppleSpeaker 소리 올린다.");
}
@Override
public void volumeDown() {
System.out.println("===> AppleSpeaker 소리 내린다.");
}
}
위와 같이 클래스를 추가해보자. 그러면 컴파일과 동시에 Speaker의 자료형을 가진 객체가 두개가 메모리에 존재한다. 따라서 오류가 난다 이럴 때 Qulifier 어노테이션으로 아래와 같이 추가해 보자
그렇다면 두 어노테이션을 해석해보자면 Autowired로 Speaker객체 가져와서 Qualifier로 Component id가 “apple”인 객체를 주입해 라고 해석할 수 있다.
4. @Primary : 위에서 처럼 Qualifier 어노테이션으로 객체를 직접 지정할 수 있지만 Primary어노테이션을 통해서 객체를 주입할 수 있다. 이 어노테이션의 의미는 같은 자료형을 가진 객체가 많이 만들어졌는데 객체에 주입하려고하면 해당 객체를 우선적으로 주입해라 라는 의미를 가지고 있다.
@Primary
@Component("apple")
public class AppleSpeaker implements Speaker {
Qualifier 어노테이션에 주석처리를 하고 AppleSpeaker에 @Primary 어노테이션을 붙여보자 결과가 잘 나오는것을 볼 수 있을 것이다.
5. @Resource : @Qualifier + @Autowired 이 두 가지의 어노테이션을 섞은 형태로 @Resource(name=”@Component id”) 형태이다.
위 예제에서는 @Resource(name=”apple”) 이런식으로 작성할 수 있을것이다.
6. @Service, @Controller, @Repository : MVC 모델에서 사용하는 어노테이션으로 모두 Component 어노테이션의 상속을 받아 객체 생성의 목적을 두고 사용하는 어노테이션으로 각자 클래스가 어떤 역할을 하는지 명시해주는 의미도 가지고 있다.
@Service ===> ServiceImpl : 비즈니스 로직을 처리하는 Service 클래스
@Repository ===> DAO : 데이터베이스 연동을 처리하는 DAO 클래스
@Controller ===> Controller : 사용자 요청을 제어하는 Controller 클래스
특히 이러한 형태로 Setter 메서드를 생성할 때 주로 사용하게 되는데 JavaScript에도 this가 있다.
JavaScript this
* 렉시컬 스코프
var x = 1;
var foo : function() {
var x = 10;
bar();
}
var bar : function() {
console.log(x);
}
foo();
bar();
위와같이 두 개의 함수를 선언하고 실행했다. 결과는 둘 다 1이 나올것이다. 이처럼 렉시컬 스코프는 어디서 실행하였는지가 중요한게 아니라 어디서 선언하였는지 중요한 것이다. foo()함수에서 실행했던간에 bar()함수는 전역에서 선언하여 전역 변수를 사용하게 된 것이다.
자바스크립트에서 this는 호출되었을때 동적으로 바인딩 된다.
foo() {
console.dir(this);
}
foo(); //window
var obj = {foo : foo;}
obj.foo(); // obj
var instance = new foo(); //instance
이런식으로 말이다. this는 기본적으로 전역변수에 바인딩 된다. 심지어 내부 함수로 설정되었더라도 일반 함수, 메소드, 콜백함수 어디에서 선언되었든 관게없이 this는 전역객체를 바인딩한다.
IoC(Inversion of Control) : 제어의 역행으로 객체의 관리를 코드가 아닌 framework이 해준다.
Container : 객체를 생성, 관리를 담당한다.
*라이브러리와 프레임워크의 차이점
라이브러리는 내가 짠 틀에 맞춰서 사용하는것이고 제어권이 없다.
프레임워크는 프레임워크가 짠 틀에 맞춰서 사용하는 것이고 제어권이 있다.
Spring의 동작과정
스프링 설정 파일( applicationContext.xml )을 로딩하여 구동하고 객체를 생성한다. (이 과정에서 container가 객체를 생성하고)
getBean(id) 메서드로 요청된 객체 요청 (id에 해당하는 객체를 요청하는구조이다.)
매핑된 해당 객체 반환 ( 따라서 객체는 기본적으로 한개이다.=singleton)
*스프링 설정 파일은 applicationContext.xml에 등록하는 것이고 ApplicationContext 그 자체는 spring or IoC Container를 뜻하는 말로 어플리케이션에서 IoC를 적용해서 관리할 모든 오브젝트에 대한 생성과 관계설정을 담당한다
기본적으로 java로 이루어진 언어이기 때문에 연산자나 기호는 java랑 비슷하다 하지만 변수 선언 만큼은 다르다.
var 변수이름 = 값;
이런 형식으로 변수를 선언하는데 java처럼 자료형을 지정하지 않는다. 그렇기 때문에 초기화와 동시에 자료형이 결정된다. 따라서 초기화하지 않고 선언만하게되면 undefined자료형을 가진다. 이런 방식의 언어를 동적타입의 언어라고 한다.
js에서 변수를 선언하는 방법은 이렇게 3가지가 있다. 세가지 변수 선언 방식이 차이점을 가지는 포인트 4가지는 재선언, 재할당, 호이스팅, 범위가 있다.
var : 재선언 가능, 재할당 가능, 호이스팅이 이루어진다, 함수 레벨 스코프다.
let : 재선언 불가능, 재할당 가능, 호이스팅이 이루어지지 않는다, 블록 레벨 스코프다.
const : 재선언 불가능, 재할당 불가능, 호이스팅이 이루어지지 않는다, 블록 레벨 스코프다.
선언하지 않음 : 선언하지 않고도 변수를 사용해서 값을 할당할 수 있다. 선언하지 않고 변수명을 통해서 값을 찾을땐 암시적 전역변수로 선언이 되어 불러진다. 이는 호이스팅이 일어나지 않으며 추후에 오류를 만들 수 있다. 따라서 'use strict'를 선언하여 엄격한 룰 적용을 하는것으로 방지할 수 있다.
* 여기서 호이스팅이란 해당 선언문을 스코프의 선두로 가져간것 처럼 동작한다는 것이다.
* javascript의 문자열은 inmmutable(변경 불가능)하므로 문자열의 일부를 수정할 수 없고 새로운 재할당을 해도 문자열이 바뀌는것이 아니라 새로운 문자열이 생성되고 참조하는 주소가 바뀌는 것이다.
* 함수 레벨 스코프는 선언된 함수 내에서 사용할 수 있다. 따라서 함수 외부에서 선언하게 되면 전역 변수가 되게 된다. 그리고 var선언 특성상 재 할당이 가능하므로 변수가 바뀌는 위험이 있을 수 있다.
* 블록 레벨 스코프는 선언된 블록 내에서 사용할 수 있다. var의 단점으로 인해 ES6부터 let과 const가 생기게 됐다.
var t = 1;
document.write(t+”<br>”); // 1
var t = 2;
document.write(t+”<br>”); // 2
t = 3;
document.write(t+”<br>”); // 3
document.write(r+”<br>”); // undefined
var r = 4;
document.write(u+”<br>”); //
이 처럼 var는 t를 두번 선언하는 재선언, t의 값 변경인 재할당, r을 아래에 선언해도 값은 할당이 안되지만 선언 자체가 가능하다. 선언 자체가 되지 않으면 u처럼 아무 결과도 나오지 않는다.
let t = 1;
document.write(t+”<br>”); // 1
let t = 2;
document.write(t+”<br>”); //
이렇게 let으로 선언하면 두 번째 let t=2에서 아무 스크립트가 나오지 않는것을 볼 수있다.
let t = 1;
document.write(t+”<br>”); // 1
t = 2;
document.write(t+”<br>”); // 2
document.write(r+”<br>”); //
let r = 3;
이처럼 재할당을 할 경우 값이 제대로 나오는 것을 볼 수 있고 호이스팅이 이루어지지 않아 변수 선언자체가 되지 않는다.
const t = 1;
document.write(t); // 1
const t = 2;
document.write(t); //
t = 3;
document.write(t); //
document.write(r); //
const r = 4;
const는 재선언, 재할당, 호이스팅이 불가능한것을 볼 수 있다.
<!DOCTYPE html>
<html>
<head>
<title>javascript</title>
<meta charset="utf-8">
</head>
<body>
a<br>
<script>
document.write("b"+"<br>");
var a,b,c;
a=5;
b=6;
c=a+b*10;
let d = 'string';
let e = ' linked string';
let f = a+" "+d;
document.write(a+"<br>");
document.write(c+"<br>");
document.write(d+"<br>");
document.write(f);
</script>
</body>
</html>
또한 js는 객체지향 언어이기 때문에 객체도 작성이 가능하다. 항상 key:value형식으로 작성해야한다.
객체 이므로 메서드 선언도 가능하다. 단, key:value 형태를 유지한다. 메서드명 : function() {}형식이다.
javascript의 자료형
javascript의 자료형으로는
number : int,float 전부 하나의 자료형으로 표현된다.
string : 문자열
null
undefined : 할당되지 않았다.
Boolean
여기까지 원시타입의 immutable(primitive)한 자료형으로 변경이 불가능하다. 실제로 값을 변경 할 수 없는것이 아니라 메모리 구조상 참조하고 있는 주소에 할당된 값을 변경할 수 없다. 따라서 재 할당시에 메모리에 새로 할당될 값이 생성되고 변수가 참조하고 있는 위치가 변경되는 것이다.
(*) 여기서 input이랑 button으로 똑같은 기능을 하는 두 개를 작성했는데 이유는 문득 input으로 만든 버튼과 button으로 만드는것이 다를까 라는 근본적인 궁금증 때문이다. input으로 만드는것으로 연습해 보자
script태그
script태그 내에 자바스크립트 코드를 작성하면 된다.
<!DOCTYPE html>
<html>
<head>
<title>javascript</title>
<meta charset="utf-8">
</head>
<body>
<h2>JavaScript in Body</h2>
<p id="demo"></p>
<script type="text/javascript">
document.write("hello");
document.getElementById("demo").innerHTML = "My First JavaScript";
</script>
</body>
</html>
그럼 결과를 볼 수 있다. 다만 script를 가장 마지막에 선언해야 한다. 이유는 모르겠다. p태그를 script태그 아래에 선언 했더니 아무것도 나오지 않는다. 그 이유는 좀 더 찾아서 새로 포스팅 해보겠다.
function(함수)
js는 객체지향의 함수이므로 이벤트 실행시에 발생하는 함수를 작성할 수 있다.
<!DOCTYPE html>
<html>
<head>
<title>javascript</title>
<meta charset="utf-8">
</head>
<body>
<h2>JavaScript in Body</h2>
<p id="demo">함수 실행 전</p>
<input type="button" value="함수 실행" onclick="func()"><br><br>
<input type="button" value="다른 함수" onclick="fun()"><br><br>
<script>
document.write("default write");
function func(){
document.getElementById("demo").innerHTML = "함수 실행 됨";
}
function fun() {
document.write("다른 함수");
}
</script>
</body>
</html>
이런 식으로 script태그 내에 선언하면 된다. 이벤트 부분에 함수를 두어 해당 이벤트 발생 시에 해당 함수가 실행된다. 여기서 document.write()를 사용해서 하면 다음에 자동으로 추가될줄 알았는데 새로운 문서가 생기고 거기에 write되는것을 확인했다. 앞으로 함수에 document.write()는 잘 안쓸것 같다.
혹은 .js 로 끝나는 확장자를 가진 파일을 만들어 연결하여 사용할 수도 있다. 마치 css적용하는것 처럼 말이다. 하지만 css와 다른점이 있다면 css는 link로 연결하고 style태그를 통해서 다른 css효과를 줄 수 있지만 script는 그렇지 않다. 두 개의 함수를 각각 하나는 script태그 내부에 하나는 js파일에 두고 실행하면 script태그 내에 있는 함수는 작동되지 않음을 볼 수 있다.