반응형

이번 포스팅은 Spring에서 Dispatcher Servlet의 정의와 기능에 대해서 알아보도록 하겠습니다.

다른 블로그에 단계별로 상세하게 설명된 글이 있어 출처를 밝히고 포워딩하도록 하겠습니다.

Reference

1. https://mangkyu.tistory.com/18


 

이번에는 servlet의 심화 또는 대표주자인 dispatcher-servlet에 대해서 알아보도록 하겠습니다.

 

1. Dispatcher-Servlet(디스패처 서블릿)의 개념


[ Dispatcher-Servlet(디스패처 서블릿) 이란? ]

디스패처 서블릿의 dispatch는 "보내다"라는 뜻을 가지고 있습니다. 그리고 이러한 단어를 포함하는 디스패처 서블릿은 HTTP 프로토콜로 들어오는 모든 요청을 가장 먼저 받아 적합한 컨트롤러에 위임해주는 프런트 컨트롤러(Front Controller)라고 정의할 수 있습니다.

이것을 보다 자세히 설명하자면, 클라이언트로부터 어떠한 요청이 오면 Tomcat(톰캣)과 같은 서블릿 컨테이너가 요청을 받게 됩니다. 그리고 이 모든 요청을 프런트 컨트롤러인 디스패처 서블릿이 가장 먼저 받게 됩니다. 그러면 디스패처 서블릿은 공통적인 작업을 먼저 처리한 후에 해당 요청을 처리해야 하는 컨트롤러를 찾아서 작업을 위임합니다.

여기서 Front Controller(프런트 컨트롤러)라는 용어가 사용되는데, Front Controller는 주로 서블릿 컨테이너의 제일 앞에서 서버로 들어오는 클라이언트의 모든 요청을 받아서 처리해주는 컨트롤러로써, MVC 구조에서 함께 사용되는 디자인 패턴입니다.

 

[ Dispatcher-Servlet(디스패처 서블릿)의 장점 ]

Spring MVC는 DispatcherServlet이 등장함에 따라 web.xml의 역할을 상당히 축소시켜주었습니다. 과거에는 모든 서블릿을 URL 매핑을 위해 web.xml에 모두 등록해주어야 했지만, dispatcher-servlet이 해당 애플리케이션으로 들어오는 모든 요청을 핸들링해주고 공통 작업을 처리면서 상당히 편리하게 이용할 수 있게 되었습니다. 우리는 컨트롤러를 구현해두기만 하면 디스패처 서블릿가 알아서 적합한 컨트롤러로 위임을 해주는 구조가 되었습니다.

 

[ 정적 자원(Static Resources)의 처리 ]

Dispatcher Servlet이 요청을 Controller로 넘겨주는 방식은 효율적으로 보입니다. 하지만 Dispatcher Servlet이 모든 요청을 처리하다 보니 이미지나 HTML/CSS/JavaScript 등과 같은 정적 파일에 대한 요청마저 모두 가로채는 까닭에 정적자원(Static Resources)을 불러오지 못하는 상황도 발생하곤 했습니다. 이러한 문제를 해결하기 위해 개발자들은 2가지 방법을 고안했습니다.

  1. 정적 자원에 대한 요청과 애플리케이션에 대한 요청을 분리
  2. 애플리케이션에 대한 요청을 탐색하고 없으면 정적 자원에 대한 요청으로 처리

 

1. 정적 자원에 대한 요청과 애플리케이션에 대한 요청을 분리

이에 대한 해결책은 두 가지가 있는데 첫 번째는 클라이언트의 요청을 2가지로 분리하여 구분하는 것입니다.

  • /apps의 URL로 접근하면 Dispatcher Servlet이 담당한다.
  • /resources의 URL로 접근하면 Dispatcher Servlet이 컨트롤할 수 없으므로 담당하지 않는다.

이러한 방식은 괜찮지만 상당히 코드가 지저분해지며, 모든 요청에 대해서 저런 URL을 붙여주어야 하므로 직관적인 설계가 될 수 없습니다. 그래서 이러한 방법의 한계를 느끼고 다음의 방법으로 처리를 하게 되었습니다.

 

2. 애플리케이션에 대한 요청을 탐색하고 없으면 정적 자원에 대한 요청으로 처리

두 번째 방법은 Dispatcher Servlet이 요청을 처리할 컨트롤러를 먼저 찾고, 요청에 대한 컨트롤러를 찾을 수 없는 경우에, 2차적으로 설정된 자원(Resource) 경로를 탐색하여 자원을 탐색하는 것입니다. 이렇게 영역을 분리하면 효율적인 리소스 관리를 지원할 뿐 아니라 추후에 확장을 용이하게 해 준다는 장점이 있습니다.

 

2. Dispatcher-Servlet(디스패처 서블릿)의 동작 과정


[ Dispatcher-Servlet(디스패처 서블릿)의 동작 방식 ]

앞서 설명한대로 디스패처 서블릿은 적합한 컨트롤러와 메서드를 찾아 요청을 위임해야 합니다. Dispatcher Servlet의 처리 과정을 살펴보면 다음과 같습니다.

 

  1. 클라이언트의 요청을 디스패처 서블릿이 받음
  2. 요청 정보를 통해 요청을 위임할 컨트롤러를 찾음
  3. 요청을 컨트롤러로 위임할 핸들러 어댑터를 찾아서 전달함
  4. 핸들러 어댑터가 컨트롤러로 요청을 위임함
  5. 비즈니스 로직을 처리함
  6. 컨트롤러가 반환 값을 반환함
  7. HandlerAdapter가 반환 값을 처리함
  8. 서버의 응답을 클라이언트로 반환함

 

아래에서는 디스패처 서블릿의 동작 과정을 조금 구체적으로 살펴보도록 하겠습니다. 만약 아래의 내용을 완벽히 이해하기 어렵다면 "디스패처 서블릿을 통해 요청을 처리할 컨트롤러를 찾아서 위임하고, 그 결과를 받아오는구나" 정도로만 이해해도 괜찮습니다.

 

1. 클라이언트의 요청을 디스패처 서블릿이 받음

앞서 설명하였듯 디스패처 서블릿은 가장 먼저 요청을 받는 프런트 컨트롤러입니다. 서블릿 콘텍스트(웹 콘텍스트)에서 필터들을 지나 스프링 콘텍스트에서 디스패처 서블릿이 가장 먼저 요청을 받게 됩니다. 이를 그림으로 표현하면 다음과 같습니다. 실제로는 Interceptor가 Controller로 요청을 위임하지는 않으므로 아래의 그림은 처리 순서를 도식화한 것으로만 이해하면 됩니다.

 

 

2. 요청 정보를 통해 요청을 위임할 컨트롤러를 찾음

디스패처 서블릿은 요청을 처리할 컨트롤러를 찾고 해당 메서드를 호출해야 합니다. HandlerMapping의 구현체 중 하나인 RequestMappingHandlerMapping은 @Controller로 작성된 모든 컨트롤러 빈을 파싱하여 HashMap으로 (요청 정보, 처리할 대상)을 관리합니다. 엄밀히는 컨트롤러가 아닌, 요청에 매핑되는 컨트롤러와 해당 메서드 등을 갖는 HandlerMethod 객체를 찾습니다. 

그래서 HadlerMapping은 요청이 오면 Http Method, URI 등을 사용해 Key 객체인 요청 정보를 만들고, Value인 요청을 처리할 HandlerMethod를 찾아 HandlerMethodExecutionChain으로 감싸서 반환합니다. HandlerMethodExecutionChain으로 감싸는 이유는 컨트롤러로 요청을 넘겨주기 전에 처리해야 하는 인터셉터 등을 포함하기 위해서입니다.

 

3. 요청을 컨트롤러로 위임할 핸들러 어댑터를 찾아서 전달함

디스패처 서블릿은 컨트롤러로 요청을 직접 위임하는 것이 아니라 HandlerAdapter를 통해 컨트롤러로 요청을 위임합니다. 이때 어댑터 인터페이스를 통해 컨트롤러를 호출하는 이유는 컨트롤러의 구현 방식이 다양하기 때문입니다. 최근에는 @Controller에  @RequestMapping 관련 어노테이션을 사용해 컨트롤러 클래스를 주로 작성하지만, Controller 인터페이스를 구현하여 컨트롤러 클래스를 작성할 수도 있습니다. 스프링은 HandlerAdapter라는 어댑터 인터페이스를 통해 어댑터 패턴을 적용함으로써 컨트롤러의 구현 방식에 상관없이 요청을 위임할 수 있는 것입니다.

 

4. 핸들러 어댑터가 컨트롤러로 요청을 위임함

핸들러 어댑터가 컨트롤러로 요청을 넘기기 전에 공통적인 전/후처리 과정이 필요합니다. 대표적으로 인터셉터들을 포함해 요청 시에 @RequestParam, @RequestBody 등을 처리하기 위한 ArgumentResolver들과 응답 시에 ResponseEntity의 Body를 Json으로 직렬화하는 등의 처리를 하는 ReturnValueHandler 등이 어댑터에서 컨트롤러로 전달되기 전에 처리됩니다. 그리고 컨트롤러의 메서드를 호출하도록 요청을 위임합니다. 실제로 요청이 위임되는 과정에서는 리플렉션이 사용됩니다.

요청을 처리할 대상 정보인 HandlerMethod 객체에는 컨트롤러 정보와 메서드 객체가 있으므로 리플렉션의 메서드 객체를 invoke 합니다. 사실 실제로는 HandlerMethod에 컨트롤러 빈 이름과 메서드, 빈 팩토리가 있어서 빈 팩토리에서 컨트롤러 빈을 찾습니다. 그리고 해당 컨트롤러 빈 객체로부터 리플렉션을 사용하는데, 그렇게 중요하지는 않으므로 따로 관심이 있는 사람들은 이 글을 참고해주세요.

 

5. 비즈니스 로직을 처리함

이후에 컨트롤러는 서비스를 호출하고 우리가 작성한 비즈니스 로직들이 진행됩니다.

 

6. 컨트롤러가 반환 값을 반환함

비즈니스 로직이 처리된 후에는 컨트롤러가 반환 값을 반환합니다. 요즘 프런트엔드와 백엔드를 분리하고, MSA로 가고 있는 시대에서는 주로 ResponseEntity를 반환합니다. 물론 컨트롤러에서 View 이름을 반환할 수도 있습니다.

 

7. HandlerAdapter가 반환 값을 처리함

HandlerAdapter는 컨트롤러로부터 받은 응답을 응답 처리기인 ReturnValueHandler가 후처리 한 후에 디스패처 서블릿으로 돌려줍니다. 만약 컨트롤러가 ResponseEntity를 반환하면 HttpEntityMethodProcessor가 MessageConverter를 사용해 응답 객체를 직렬화하고 응답 상태(HttpStatus)를 설정합니다. 만약 컨트롤러가 View 이름을 반환하면 ViewResolver를 통해 View를 반환합니다.

 

8. 서버의 응답을 클라이언트로 반환함

디스패처 서블릿을 통해 반환되는 응답은 다시 필터들을 거쳐 클라이언트에게 반환됩니다.

 

이번 포스팅에서는 디스패처 서블릿에 대해 이해해보았습니다. 위의 과정 역시 상당히 많은 부분을 추상화하고 생략하여 핵심 부분만을 설명하였는데, 실제 과정은 훨씬 복잡하고 많은 처리들이 존재합니다. 최대한 핵심만 쉽게 전달하려고 했는데 잘 전달되었는지 모르겠네요!

보다 더 깊게 디스패처 서블릿의 실제 소스 코드를 보면서 분석해보고 싶으신 분들은 이 글을 참고해주세요! 이 글에서는 디스패처 서블릿의 동작 과정과 함께 적용된 디자인 패턴들(전략 패턴, 템플릿 메서드 패턴, 컴포지트 패턴)을 살펴볼 수 있습니다.

감사합니다!

 

출처: https://mangkyu.tistory.com/18 [MangKyu's Diary:티스토리]


 

반응형

'Development > Web' 카테고리의 다른 글

[Web] Spring Framework vs Spring Boot  (0) 2022.09.24
[Web] Spring Framework의 특징  (0) 2022.09.24
[Web] 크로스 도메인  (0) 2022.09.23
[Web] 뷰 컴포넌트 통신  (0) 2022.09.19
[Web] 뷰 컴포넌트  (0) 2022.09.18
반응형

#. Spring Framework의 다양한 모듈


Spring Framework는 기능별로 구분된 약 20여 개의 모듈로 구성되어 있습니다. 아래 그림은 스프링 공식문서에서 제공하는 다이어그램을 재구성한 것입니다.

Spring Framework 공식 문서에서는 Spring 버전 별로 다른 다이어그램을 제시하고 있지만 큰 틀은 유사합니다. 그리고 Spring Framework를 사용한다고 해서 모든 모듈을 사용할 필요는 없습니다. 애플리케이션 개발에 필요한 모듈만 선택해서 사용하게끔 설계되어 있으며, 이를 경량 컨테이너 설계라고 부릅니다.

 

#2. Spring Framework VS Spring Boot


앞에서 살펴본 것처럼 Spring Framework는 기존 개발 방식의 문제와 한계를 극복하기 위해서 다양한 기능을 제공합니다. 하지만 기능이 많은 만큼 필요한 모듈들을 추가하다 보면 설정이 복잡해지는 문제가 있습니다. 이러한 문제를 해결하기 위해 등장한 것이 스프링 부트(Spring Boot)입니다. Spring Boot 공식 사이트에는 다음과 같은 문구가 쓰여 있습니다.

Spring Boot make it easy to create stand-alone, production-grade Spring based Application that you can "just run".

 위 문구를 해석하면 "String Boot를 이용하면 단독으로 실행 가능한 상용 수준의 Spring 기반 애플리케이션을 을 쉽게 만들 수 있습니다." 즉, 별도의 복잡한 설정을 하지 않아도 Spring Boot를 사용하면 개발이 쉬워진다는 뜻입니다.

이어서 Spring Framework와 비교하였을 때 Spring Boot가 가진 특징을 알아보겠습니다.

 

의존성 관리

Spring Framework에서는 개발에 필요한 각 모듈의 의존성을 직접 설정해야 했습니다. 또 호환되는 버전을 명시해야 정상 동작합니다. 애플리케이션에서 사용하는 Spring Framework나 Library의 버전을 올리는 상황에서는 연관된 다른 Library의 버전까지도 고려해야 합니다.

하지만 Spring Boot에서는 이 같은 불편함을 해소하기 위해 'spring-boot-starter'라는 의존성을 제공합니다. spring-boot-starter의 의존성은 여러 종류가 있고, 각 Library의 기능과 관련해서 자주 사용되고 서로 호환되는 버전의 모듈 조합을 제공합니다. 이를 통해 개발자는 Libary 호환 문제를 해결할 수 있습니다. 

많이 사용하는 spring-boot-starter 라이브러리를 간략하게 소개하자면 다음과 같습니다.

- spring-boot-starter-web : Spring MVC를 사용하는 RESTful 애플리케이션을 만들기 위한 의존성입니다. 기본으로 내장 Tomcat이 포함되어 있어 jar 형식으로 실행 가능합니다.

- spring-boot-starter-test : JUnit, Jupiter, Mochito 등의 테스트용 Library를 포함합니다.

- spring-boot-starter-jdbc : HikariCP Connection Pool를 활용한 JDBC 기능을 제공합니다.

- spring-boot-starter-security : Spring Security(인증, 권한, 인가 등 기능을 제공합니다.

- spring-boot-starter-data-jpa : 하이버네이트를 활용한 JPA 기능을 제공합니다.

- spring-boot-starter-cache : Spring Framework의 캐시 기능을 제공합니다.

▶'spring-boot-starter'의 여러 라이브러리를 함께 사용할 때는 의존성이 겹칠 수 있습니다. 이로 인해 버전 충돌이 발생할 수 있는데, 의존성 조합 충돌 문제가 없도록 '- spring-boot-starter-parent'가 검증된 조합을 제공합니다.

 

자동 설정

Spring Boot는 Spring Framework의 기능을 사용하기 위한 자동 설정(Auto Configuration)을 지원합니다. 자동 설정은 애플리케이션에 추가된 Library를 실행하는 데 필요한 환경 설정을 알아서 찾아줍니다. 즉, 애플리케이션을 개발하는 데 필요한 의존성을 추가하면 프레임워크가 이를 자동으로 관리해줍니다. 예를 들어, Spring Boot 프로젝트를 생성하면 아래와 같은 애플리케이션 코드를 볼 수 있습니다.

@SpringBootApplication
public class SpringBootApplication {

      public static void main(String[] args) {
               SpringApplication.run (SpringBootApplication.class, args);
      }
}

여기서 살펴볼 것은 @StringBootApplication Annotation입니다. 이 Annotation은 여러 Annotation을 합쳐 놓은 인터페이스이지만 기능 위주로 보면 크게 다음 세 개의 Annotation을 합쳐 놓은 구성입니다.

- @SpringBootConfiguration

- @EnableAutoConfiguration

- @ComponentScan

Spring Boot Applicaton이 실행되면 우선 @ComponentScan Annotation이 @Component 시리즈 Annotation이 붙은 클래스를 발견해 빈(Bean)을 등록합니다. 이후 @EnableAutoConfiguration Annotation을 통해 'spring-boot-autoconfiguration' 패키지 안에 spring.factories 파일을 추가해 다양한 자동 설정이 일부 조건을 거쳐 적용이 됩니다. 이 설정은 각 파일에서 설정된 @Conditional의 조건을 충족할 경우 빈(Bean)에 등록되고 애플리케이션에 자동 반영됩니다.

▶@Component 시리즈 Annotation에서 시리즈는 @Component Annotation이 포괄하는 Annotation을 통칭하기 위해 사용한 표현입니다. 이러한 @Component 시리즈 Annotation의 대표적인 예는 다음과 같습니다.

- @Controller, @RestController, @Service, @Repository, @Configuration

 

내장 WAS

Spring Boot의 각 웹 애플리케이션에는 내장 WAS(Web Application Server)가 존재합니다. 웹 애플리케이션을 개발할 때 가장 기본이 되는 의존성인 'spring-boot-starter-web'의 경우 Tomcat을 내장합니다.

Spring Boot의 자동 설정 기능은 Tomcat에도 적용되므로 특별한 설정 없이도 Tomcat을 실행할 수 있습니다. 필요에 따라서는 Tomcat이 아닌 다른 웹 서버(Jetty, Undertow 등)로 대체할 수 도 있습니다.

 

모니터링

개발이 끝나고 서비스를 운영하는 시기에는 해당 시스템이 사용하는 스레드, 메모리, 세션 등의 주요 요소들을 모니터링해야 합니다. Spring Boot에는 스프링 부트 액츄에이터(Spring Boot Actuator)라는 자체 모니터링 도구가 있습니다. 

 

Reference1. 장정우 [스프링부트 핵심 가이드]

반응형

'Development > Web' 카테고리의 다른 글

[Web] Spring의 Dispatcher Servlet의 정의와 기능  (0) 2022.09.24
[Web] Spring Framework의 특징  (0) 2022.09.24
[Web] 크로스 도메인  (0) 2022.09.23
[Web] 뷰 컴포넌트 통신  (0) 2022.09.19
[Web] 뷰 컴포넌트  (0) 2022.09.18
반응형

#1. Spring Framework 정의


스프링 프레임워크(Spring Framework)란 자바(Java) 기반의 애플리케이션 프레임워크로 엔터프라이즈급 애플리케이션을 개발하기 위한 다양한 기능을 제공합니다. 쉽게 말해 Java로 애플리케이션을 개발하는 데 필요한 기능을 제공하고 쉽게 사용할 수 있도록 돕는 도구입니다

▶ 엔터프라이즈급 개발은 기업 환경을 대상으로 하는 개발을 뜻합니다. 네이버나 카카오톡 같은 대규모 데이터를 처리하는 환경을 엔터프라이즈 환경이라고 부릅니다. 스프링은 이 환경에 알맞게 설계되어 있어 개발자는 애플리케이션을 개발할 때 많은 요소를 프레임워크에 위임하고 비즈니스 로직을 구현하는데 집중할 수 있습니다.

스프링의 핵심가치는 아래와 같습니다.

어플리케이션 개발에 필요한 기반을 제공해서 개발자가 비즈니스 로직 구현에만 집중할 수 있게끔 하는 것

 

#2. Spring Framework의 특징


Spring Framework의 특징은 크게 3가지로 분류할 수 있습니다.

1) Ioc (Inversion of Control : 제어 역전) 

   - Spring Framework는 기존 Java 개발 방식과 다르게 동작합니다. Ioc를 적용한 환경에서는 사용할 객체를 직접 생성하지 않고 객체의 생명주기 관리를  스프링 컨테이너(Spring Container)에 위임합니다. 객체의 관리를 컨테이너에 맡겨 제어권이 넘어간 것을 제어 역전이라고 부르며, 제어 역전을 통해 뒤에 나올 DI (Dependency Injection : 의존성 주입),  AOP(Aspect-Oriented Programming : 관점 지향 개발) 등이 가능해집니다. 

2) DI (Dependency Injection : 의존성 주입)

  - Ioc(Inversion of Control : 제어 역전)의 제어 역전 방법 중 하나로, 사용할 객체를 직접 생성하지 않고 외부 컨테이너가 생성한 객체를 주입받아 사용하는 방식을 의미합니다.

  - Spring Framework에서는 의존성을 주입하는 방법은 3가지가 있습니다. 그리고 스프링 공식 문서에서 권장하는 의존성 주입 방법은 생성자를 통해 의존성을 주입받는 방식입니다. 이는 다른 방식과 달리 레퍼런스 객체 없이는 객체를 초기화할 수 없게 설계할 수 때문입니다.

    > 생성자를 통한 의존성 주입

    > 필드 객체를 통한 의존성 주입

    > Setter 메서드를 통한 의존성 주입

3) AOP(Aspect-Oriented Programming : 관점 지향 개발)

   - Spring Framework의 아주 중요한 특징으로써 AOP는 관점을 기준으로 묶에 개발하는 방식을 의미합니다. 여기서 관점(Aspect)란 어떤 기능을 구현할 때 그 기능을 '핵심 기능'과 '부가 기능'으로 구분해 각각을 하나의 관점으로 보는 것을 의미합니다. 그럼 '핵심 기능'은 비즈니스 로직을 구현하는 과정에서 비즈니스 로직이 처리하려는 목적 기능이고, '부가 기능'은 Log, Trasaction과 같이 핵심 기능이 어떤 기능인지에 무관하게 로직이 수행되기 전 또는 후에 수행하기만 하면 되는 기능입니다.

  - AOP는 여러 비즈니스 로직에서 부가 기능을 하나의 공통 로직으로 처리하도록 모듈화해 삽입하는 방식을 말합니다. 이러한 AOP를 구현하는 방법은 3가지가 있습니다. 이 가운데 스프링은 디자인 패턴 중 하나인 Proxy(프락시) 패턴을 통해 AOP 기능을 제공하고 있습니다.

   > 컴파일 과정에 삽입하는 방식

   > 바이트 코드를 메모리에 로드하는 과정에 삽입하는 방식

   > Proxy(프락시) 패턴을 이용한 방식

- Spring Framework AOP의 목적은 모듈화해서 재사용 가능한 구성을 만드는 것이고, 모듈화된 객체를 편하게 적용할 수 있게 함으로써 개발자가 비즈니스 로직을 구현하는데만 집중할 수 있게 도와주는 것입니다. 

 

Reference

1. 장정우 [스프링부트 핵심 가이드]

반응형

'Development > Web' 카테고리의 다른 글

[Web] Spring의 Dispatcher Servlet의 정의와 기능  (0) 2022.09.24
[Web] Spring Framework vs Spring Boot  (0) 2022.09.24
[Web] 크로스 도메인  (0) 2022.09.23
[Web] 뷰 컴포넌트 통신  (0) 2022.09.19
[Web] 뷰 컴포넌트  (0) 2022.09.18
반응형

 

#. 크로스 도메인(Cross Domain)의 정의


크로스 도메인의 정의는 '서로 다른 도메인(Domain) 간의 호출'입니다.

만약, 제가 개발한 웹 사이트에서만 사용하기 위해서 API 서버를 구축하였는데, 다른 웹 서비스에서 이 API 서버에 마음대로 접근하여 API를 호출하여 사용한다면 보안상 큰 문제가 되어 이를 차단합니다. 그래서 Javascript는 Same Origin Policy(동일한 출처 정책)을 두어 다른 도메인 서버에 요청(Request) 하는 것을 차단합니다. 다시 말해 Javascript는 동일한 도메인 내에서만 서버 요청을 허용한다는 것입니다.

이러한 크로스 도메인 이슈는 아래와 같이 A, B 페이지 간에 통신을 하려고 할 때 발생할 수 있습니다. 예를 들어 설명을 하겠습니다.

1. 아래와 같이 도메인이 다른 경우

A : 네이버(www.naver.com) / B : 구글(www.google.com)

2. 아래와 같이 메인 도메인은 같지만 서브 도메인이 다른 경우

A : 네이버 카페(cafe.naver.com) / 네이버 블로그(blog.naver.com)

3. 아래와 같이 포트 번호가 서로 다른 경우

A : 네이버(www.naver.com:8080) / B : 네이버(www.naver.com:8088)

4. 아래와 같이 프로토콜이 상이한 경우

A : HTTP 네이버(http://www.naver.com) / HTTPS 네이버(https://www.naver.com) 

 

동일한 도메인 내에서만 통신을 할 수는 없는 노릇이고 개발을 진행하다 보면 분명 다른 도메인과 통신이 필요한 경우가 생기게 되고 이를 해결하기 위해 크로스 도메인을 허용해야 경우가 있습니다.

 

#. 크로스 도메인을 허용해야 하는 이유


 

 

예를 들어, 하나의 웹 사이트에서 효율성이나 성능 등의 이유로 각 기능별로 여러 서버를 두는 경우가 많습니다. (API 서버, WAS 서버, 파일 서버 등) 그런데 이 서버들은 물리적으로 분리된 서버이고, 다른 용도로 구축된 서버이기 때문에 당연히 각각 다른 도메인을 가진 서버들입니다. 그래서 크로스 도메인 이슈(다른 도메인의 서버의 URL을 호출하여 데이터를 가져올 수 없다.)가 발생하기 때문에 서로 간의 AJAX 통신을 할 수가 없습니다. 그래서 크로스 도메인(서로 다른 도메인 간의 호출)을 허용해야 하는 경우가 생깁니다.

그래서 고안된 게 크로스 도메인을 허용하는 방법입니다. 크로스 도메인을 활성화시키는 방법은 약 3가지가 있으며 방법은 아래와 같습니다.

 

첫 번째 방법은 Javascript의 document.domain을 이용하는 것입니다. 참고로 이는 서브 도메인 간의 크로스 도메인 문제 해결에만 사용될 수 있는 방법입니다.

예를 들어 http://cafe.naver.com/test.html에서 http://blog.naver.com/test.html에에 접근한다고 하면 각 소스의 Javascript 부분에 아래와 같이 명시해서 도메인 값을 맞춰줍니다.

<script type="text/javascript">
     document.domain = 'naver.com';
</script>

 

두 번째 방법은 jsonp를 이용하는 것입니다. 이 방법은 서브 도메인 외에도 사용할 수 있는데 아래 두 가지 특성을 이용한 일종의 편법적인 방법입니다.

  - HTML의 scipt 요소로부터 요청되는 호출에는 Same Origin Policy(동일 출처 정책) 정책이 적용되지 않음

  - script 요소는 src를 호출한 결과를 javascript를 불러와서 포함시키는 것이 아니고 실행시키는 태그임

<script src="http://code.jquery.com/jquery-latest.js"></script>

예를 들어 위 코드는 jquery를 링크하는데 code.jquery.com에서 항상 최신 버전을 받아오도록 하는 코드입니다.

제가 개발하는 도메인과 다른데도 전혀 문제가 되지 않습니다. Same Origin Policy(동일 출처 정책) 정책이 적용되지 않기 때문입니다.

클라이언트 측은 jquery를 이용해서 아래와 같이 간단하게 구현할 수 있습니다.

$.ajax ( {
    url : "http://cafe.naver.com/useSearch",
    data : "id=user";
    dataType : "jsonp",
    jsonCallback : "callbackOfJsonp",
    success : function(response) {
            /* data는 JSON 객체임 */
            console.log(response);
    }
} )

서버 측에서도 별도의 작업이 필요합니다.

"script 요소는 src를 호출한 결과를 javascript를 불러서 포함시키는 것이 아니고 실행시키는 태그이다."라는 특성을 이용해 실행될 함수 자체를 문자열로 완성해서 리턴해주어야 하는데 위 AJAX Jsonp 처리를 위해서는 결국 서버의 Controller에서 아래와 같은 문자영을 리턴해주어야 합니다.

"callbackOfJsonp ("name", "test", "ageL28")

이렇게 되면 $.ajax 에서는 jsonpCallback 요소로 지정된 함수명과 일치하는 함수 문자열을 success 요소에 바인딩해서 처리되도록 해줍니다.

이를 위해서 데이터는 Json 문자열 포맷으로 하고 그것을 다시 특정 함수로 감싸줘야 합니다.

일반적으로 Jackson 라이브러리와 @RestController를 많이 사용하기 때문에 Json 포맷을 리턴하는 것은 문제가 되지 않지만 위와 같이 함수명을 감싸려면 아래와 같이 @ControllerActive를 만들어놔야 합니다.

@ControllerActive
public class JsonAdviceController extends AbstractJsonResponseBodyAdvice {
       public JsonAdviceController () {
               super ("callback");
      }
}

 

세 번째 방법은 CORS를 이용하여 핸들링하는 것입니다.

CORS (Cross-Origin Resources Sharing)의 약자로써 HTML5에서 스펙이 정의되었으며, IE8이상의 모든 브라우저에서 구현되어 있습니다. CORS는  서버와 클라이언트가 정해진 헤더를 통해서 서로 요청이나 응답에 반응할지 결정하는 방식으로써 웹 페이지의 제한된 자원을 외부 도메인에서의 접근 및 요청을 허용해 주는 것입니다. CORS를 이용하여 특정 도메인에서 접근하는 것이 가능하도록 허용해 줌으로써 서로 다른 도메인 간의 호출이 가능하게 됩니다.

동작 방식은 아래와 같습니다.

이처럼 요청하는 URL이 외부 도메인일 경우 웹 브라우저는 OPTION으로 preflight 요청을 먼저 날려고 요청할 수 있는 권한이 있는지 확인합니다. 서버 단에서는 이렇게 날아온 preflight 요청을 처리하여 웹 브라우저에서 실제 요청을 날릴 수 있도록 해주면 됩니다.

일반적으로 filter나 interceptor로 처리하여 Spring에서 filter 기준 아래와 같이 구현합니다.

public class CORSFilter extends OncePerRequestFilter {

    @override
    protected void doFilterInternal (HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
    throws ServletException {
          response.addHeader ("Accept-Control-Allow-Origin", "*");
          response.addHeader ("Accept-Control-Allow-Methods", "POST,GET,OPTIONS,DELETE");
          response.addHeader ("Accept-Control-Max-Age", "3600");
          response.addHeader ("Accept-Control-Allow-Headers", "X-Requested-With");

          filterChain.doFilter (request, response);

    }
}

Reference

1. [JavaScript] 크로스 도메인(Cross Domain) : 네이버 블로그 (naver.com)

2. 크로스 도메인(cross-domain) 이슈 : 네이버 블로그 (naver.com)

반응형

'Development > Web' 카테고리의 다른 글

[Web] Spring Framework vs Spring Boot  (0) 2022.09.24
[Web] Spring Framework의 특징  (0) 2022.09.24
[Web] 뷰 컴포넌트 통신  (0) 2022.09.19
[Web] 뷰 컴포넌트  (0) 2022.09.18
[Web] 뷰 인스턴스 라이프 사이클  (0) 2022.09.18
반응형

이번 포스팅은 뷰 컴포넌트의 통신에 대하여 알아보도록 하겠습니다.

 

#1. 컴포넌트 간 통신과 유효 범위


앵귤러1이나 백본(backbone.js)과 같은 초창기 자바스크립트 프레임워크에서는 한 화면을 1개의 뷰로 간주했습니다. 따라서 한 화면의 데이터를 해당 화면 영역 어디서든지 호출할 수 있었죠. 하지만 뷰(Vue.js)의 경우 컴포넌트로 화면을 구성하므로 같은 웹 페이지라도 데이터를 공유할 수 없습니다. 그 이유는 컴포넌트마다 자체적으로 고유한 유효 범위(Scope)를 갖기 때문입니다. 이는 뷰 프레임워크 내부적으로 정의된 특징입니다. 따라서 각 컴포넌트의 유효 범위가 독립적이기 때문에 다른 컴포넌트의 값을 직접적으로 참조할 수가 없습니다.

다음 예제를 보겠습니다.

<div id="app">
   <my-component1> </my-component1>
   <my-component2> </my-component2>
</div>

<script>
   // 첫 번째 컴포넌트 내용
   var cmp1 = {
      template : '<div>첫 번째 컴포넌트 : {{ cmp1Data }}</div>'
      data : function() {
          return {
               cmp1Data : 100
          }
      }
   };
   //  두 번째 컴포넌트 내용
   var cmp2 = {
      template : '<div>두 번째 컴포넌트 : {{ cmp2Data }}</div>'
      data : function() {
          return {
               cmp1Data : cmp1.data.cmp1Data
          }
      }
   };
   new Vue({
       el : '#app',
       components : {
            'my-component1' : cmp1,
            'my-component2' : cmp2  
       }
   });
</script>

이 예제는 2개의 지역 컴포넌트를 등록하고, 한 컴포넌트에서 다른 컴포넌트의 값을 직접 참조하는 예제입니다. my-component2 컴포넌트 내용에서  {{ cmp2Data }}는 my-component1컴포넌트의 data.cmp1Data를 참조하고 있습니다. 자바스크립트의 객체 참조 방식을 생각해 보면 참조값 100이 화면에 표시되어야 합니다. 하지만 {{ cmp2Data }}는 아무것도 표시하지 않습니다. 이 예제를 실행하면 다음과 같은 결과 화면이 나옵니다.

첫 번째 지역 컴포넌트 : 100
두 번째 지역 컴포넌트 : 

{{ cmp2Data }}에 아무 값도 출력되지 않은 이유는 my-component2에서 my-component1의 값을 직접 참조할 수 없기 때문입니다. 즉, 앞에서 언급했듯이 컴포넌트의 유효 범위로 인해 다른 컴포넌트의 값을 직접 접근하지 못하기 때문에 나타난 결과입니다. 이렇게 다른 컴포넌트의 값을 참조하지 못하기 때문에 생기는 특징도 있습니다. 뷰에서 미리 정의해 놓은 데이터 전달 방식에 따라 일관된 구조로 애플리케이션을 작성하게 됩니다. 그러므로 개발자 개개인의 스타일대로 구성되지 않고, 애플리케이션이 모두 동일한 데이터 흐름을 갖습니다. 이렇게 되면 다른 사람의 코드를 빠르게 파악할 수 있어 협업에도 좋습니다.

 

#2. 상.하위 컴포넌트 관계


앞에서 살펴본 것처럼 컴포넌트는 가각가 고유한 유효 범위를 갖고 있기 때문에 직접 다른 컴포넌트의 값을 참조할 수 없습니다. 따라서 뷰 프레임워크 자체에서 정의한 컴포넌트 데이터 전달 방법을 따라야 합니다. 가장 기본적인 데이터 전달 방법은 바로 상위(부모)-하위(자식) 컴포넌트 간의 데이터 전달 방법입니다.

상위-하위 컴포넌트란 트리 구조에서 부모 노드, 자식 노드처럼 컴포넌트 간의 관계가 부모, 자식으로 이루어진 컴포넌트를 의미합니다. 컴포넌트 등록 방법으로 지역 또는 전역 컴포넌트를 등록하면 등록된 컴포넌트는 자연스럽게 하위 컴포넌트(자식 컴포넌트)가 됩니다. 그리고 하위 컴포넌트를 등록한 인스턴스는 상위 컴포넌트(부모 컴포넌트)가 됩니다.

다음 그림은 뷰에서 상위-하위 컴포넌트 간에 데이터를 전달하는 기본적인 구조를 나타냅니다.

먼저 상위에서 하위로는 props라는 특별한 속성을 전달합니다. 그리고 하위에서 상위로는 기본적으로 이벤트만 전달할 수 있습니다. 그러면 각 전달 방법에 대해 살펴보겠습니다.

▶ 이벤트와 함께 데이터를 전달하고 싶은 경우에는 이벤트의 두 번재 인자 값으로 전달하거나 이벤트 버스(Event Bus)를 활용하는 방법이 있습니다.

 

#3. 상위에서 하위 컴포넌트로 데이터 전달하기


props 속성

props는 상위 컴포넌트에서 하위 컴포넌트로 데이터를 전달할 때 사용하는 속성입니다. props 속성을 사용하려면 먼저 오른쪽처럼 하위 컴포넌트의 속성에 정의합니다.

Vue.component ('child-component', {
     props : ['props 속성 이름']
});

그런 다음 상위 컴포넌트의 HTML코드에 등록된 child-component컴포넌트 태그에 v-bind 속성을 추가합니다.

<child-component v-bind:props 속성 이름="상위 컴포넌트의 data 속성"> </child-component>

v-bind 속성의 왼쪽 값으로 하위 컴포넌트에서 정의한 props 속성을 넣고, 오른쪽 값으로 하위 컴포넌트에 전달할 상위 컴포넌트의 data 속성을 지정합니다.

그러면 props 속성을 사용해서 데이터를 전달하는 예제를 살펴보겠습니다.

이 코드는 상위 컴포넌트의 message 속성을 하위 컴포넌트에 props로 전달하여 메시지를 출력하는 예제입니다. props 속성을 이해하기 위해 코드를 작성한 순서대로 살펴보겠습니다.

1) new Vue() 인스턴스를 하나 생성합니다.

2) Vue.component()를 이용하여 하위 컴포넌트인 child-component를 등록합니다.

3) child-component의 내용에 props속성으로 propsdata를 정의합니다.

4) HTML에 컴포넌트 태그를 추가합니다. <child-component> 태그의 v-bind 속성을 보면, v-bind:prosdata="message"는 상위 컴포넌트의 message 속성 값인 Hello Vue! passed from parent Component 텍스트를 하위 컴포넌트의 prosdata로 전달하였습니다.

5) child-component의 template 속성에 정의된 <p>{{ propsdata }}</p>는 Hello Vue! passed from Parent Component가 됩니다.

위 내용을 더 간단히 정리하면 뷰 인스턴스의 data 속성에 정의된 message 속성을 하위 컴포넌트에 props로 전달하여 화면에 나타냅니다.

브라우저로 실핸한 결과 화면은 다음과 같습니다.

Hello Vue! passed from Parent Component

여기서 한 가지 짚고 넘어가야 할 부분이 있습니다. 바로 컴포넌트 간의 관계입니다.

예제 코드에서는 child-component를 전역으로 등록한 것 이외에 딱히 상위 컴포넌트를 지정하지 않았습니다. 그럼에도 불구하고 뷰 인스턴스 안에 마치 상위 컴포넌트가 존재하는 것처럼 하위 컴포넌트로 props를 내려보냈습니다. 그 이유는 컴포넌트를 등록함과 동시에 뷰 인스턴스 자체가 상위 컴포넌트가 되기 때문입니다. 관계를 정확히 이해하기 위해 다음 그림을 살펴보겠습니다.

이렇게 인스턴스에 새로운 컴포넌트를 등록하면 기존에 있는 컴포넌트는 상위 컴포넌트(부모)가 되고, 새로 등록된 컴포넌트는 하위(자식) 컴포넌트가 됩니다. 그리고 이렇게 새 컴포넌트를 등록한 인스턴스를 최상위 컴포넌트(Root Component)라고도 부릅니다.

 

#4. 하위에서 상위 컴포넌트로 이벤트 전달하기


이벤트 발생과 수신

앞에서 배운 props는 상위에서 하위 컴포넌트로 데이터를 전달하는 방식입니다. 그럼 반대로 하위 컴포넌트에서 상위 컴포넌트로의 통신은 어떻게 할까요? 이벤트를 발생시켜(event emit) 상위 컴포넌트에 신호를 보내면 됩니다. 상위 컴포넌트에서 하위 컴포넌트의 특정 이벤트가 발생하기를 기다리고 있다가 하위 컴포넌트에서 특정 이벤트가 발생하면 상위 컴포넌트에서 해당 이벤트를 수신하여 상위 컴포넌트의 메서드를 호출하는 것입니다.

▶뷰 공식 사이트의 이벤트 발생 사용 방법에서는 하위에서 상위로 데이터를 전달하는 방법을 다루지 않습니다. 왜냐하면 이는 뷰의 단방향 데이터 흐름에 어긋나는 구현 방법이기 때문이죠. 하지만 향후에 복잡한 뷰 애플리케이션을 구축할 때 이벤트 버스(Event Bus)를 이용하여 데이터를 전달해야 할 경우가 있기 때문에 이벤트 인자로 데이터를 전달하는 방법에 대해서는 별도로 포스팅 하도록 하겠습니다.

이벤트 발생과 수신 형식

이벤트 발생과 수신은 $emit()과 v-on: 속성을 사용하여 구현합니다. 각각 형식은 아래와 같습니다.

// 이벤트 발생
this.$emit('이벤트명');

// 이벤트 수신
<child-component v-on:이벤트명="상위 컴포넌트의 메소드명"></child-component>

$emit()을 호출하면 괄호 안에  정의된 이벤트가 발생합니다. 그리고 일반적으로 $emit()을 호출하는 위치는 하위 컴포넌트의 특정 메소드 내부입니다. 따라서 $emit()을 호출할 때 사용하는 this는 하위 컴포넌트를 가리킵니다.

호출한 이벤트는 하위 컴포넌트를 등록하는 태그(상위 컴포넌트의 template속성에 위치)에서 v-on:으로 받습니다. 하위 컴포넌트에서 발생한 이벤트명을 v-on: 속성에 지정하고, 속성의 값에 이벤트가 발생하였을 때 호출될 상위 컴포넌트의 메서드를 지정합니다.

 

Reference

1. 장기효 [예제로 이해하고 실전 프로젝트로 완성한다! Vue.js 입문]

반응형

'Development > Web' 카테고리의 다른 글

[Web] Spring Framework의 특징  (0) 2022.09.24
[Web] 크로스 도메인  (0) 2022.09.23
[Web] 뷰 컴포넌트  (0) 2022.09.18
[Web] 뷰 인스턴스 라이프 사이클  (0) 2022.09.18
[Web] 뷰 인스턴스  (0) 2022.09.18
반응형

이번 포스팅은 뷰 컴포넌트에 대하여 알아보도록 하겠습니다.

 

#1. 컴포넌트


컴포넌트(Component)란 조합하여 화면을 구성할 수 있는 블록(화면의 특정 영역)을 의미합니다. 컴포넌트를 활용하면 화면을 빠르게 구조화하여 일괄적인 패턴으로 개발할 수 있습니다. 이렇게 화면의 영역을 컴포넌트로 쪼개서 재활용할 수 있는 형태로 관리하면 나중에 코드를 다시 사용하기가 훨씬 편리합니다. 또한 모든 사람들이 정해진 방식대로 컴포넌트를 등록하거나 사용하게 되므로 남이 작성한 코드를 직관적으로 이해할 수 있습니다.

뷰에서는 웹 화면을 구성할 때 흔히 사용하는 내비게이션 바(Navigation bar), 테이블(Table), 리스트(List), 인풋 박스(Inout box) 등과 같은 화면 구성 요소들을 잘게 쪼개어 컴포넌트로 관리합니다. 다음 그림에서 왼쪽은 웹 페이지 한 화면의 영역을 각각 역할별로 분할한 그림이고, 오른쪽은 각각 분할된 영역 간의 관계를 도식화한 그림입니다. 여기서 각각 분할된 영역은 컴포넌트를 의미합니다.

위의 왼쪽 그림은 화면 전체를 Header, Content, Footer로 분할하였고, Content 영역을 Aside, List 영역으로 분할하였습니다. 이는 화면 전체를 3개의 컴포넌트로 분할한 후 분할된 1개의 컴포넌트에서 다시 2개의 하위 컴포넌트로 분할한 것입니다. 그리고 오른쪽 그림은 각 컴포넌트 간의 관계를 나타냅니다. 이러한 컴포넌트 간의 관계는 뷰에서 화면을 구성하는 데 매우 중요한 역할을 하며, 웹 페이지 화면을 설계할 때도 이와 같은 골격을 유지하면서 설계를 해야 합니다. 참고로 컴포넌트 간의 관계는 자료구조의 트리(Tree) 모양과 유사합니다.

 

#2. 컴포넌트 등록하기


컴포넌트를 등록하는 방법은 전역과 지역의 두 가지가 있습니다. 지역(Local) 컴포넌트는 특정 인스턴스에서만 유효한 범위를 갖고, 전역(Global) 컴포넌트는 여러 인스턴스에서 공통으로 사용할 수 있습니다. 더 쉽게 말하자면 지역은 특정 범위 내에서만 사용할 수 있고, 전역은 뷰로 접근 가능한 모든 범위에서 사용할 수 있다는 거죠. 그럼 두 가지 방법에 대해 살펴보겠습니다.

전역(Global) 컴포넌트 등록하기

전역 컴포넌트는 뷰 라이브러리를 로딩하고 나면 접근 가능한 Vue 변수를 이용하여 등록합니다. 전역 컴포넌트를 모든 인스턴스에 등록하려면 Vue 생성자에서 .component()를 호출하여 수행하면 됩니다. 형식은 아래와 같습니다.

Vue.component('컴포넌트 이름', {
       // 컴포넌트 내용
});

전역 컴포넌트 등록 형식에는 컴포넌트 이름과 컴포넌트 내용이 있습니다. 컴포넌트 이름은 template 속성에서 사용할 HTML 사용자 정의 태그(custom tag) 이름을 의미합니다. 태그 이름의 명명 규칙은 HTML 사용자 정의 태그 스펙에서 강제하는 '모든 소문자'와 '케밥 기법'을 따르지 않아도 됩니다.

그리고 컴포넌트 태그가 실제 화면의 HTML 요소로 변환될 때 표시될 속성들을 컴포넌트 내용에 작성합니다. 컴포넌트 내용에는 template, data, methods 등 인스턴스 옵션 속성을 정의할 수 있습니다.

▶ 사용자 정의 태그 : HTML 표준 태그들 이외에도 웹 개발자가 직접 정의하여 사용할 수 있는 태그

▶ 케밥 기법 : 변수가 단어의 조합으로 이루어져 있을 때, 단어와 단어 사이를 -로 잇는 변수 명명 방식 ex) my-component, my- global-component 등

그럼 Vue.component()로 전역 컴포넌트를 1개 등록하고 화면에 그리는 예제를 살펴보겠습니다.

<html>
  <head>
    <title>Vue Component Registration</title>
  </head>
  <body>
    <div id="app">
       <button>컴포넌트 등록</button>
       <my-component></my-component>  ----------------------------- 전역 컴포넌트 표시
    </div>

    <script src="https://cdn.jsdeliver.net/npm/vue@2.5.2/dist/vue.js"></script>
    <script>
       Vue.component('my-component', {
           template : '<div>전역 컴포넌트가 등록되었습니다.</div>
        });  ---------------------------------------------------------------------------- 전역 컴포넌트 등록

        new Vue ({
           el : '#app'
        });
    </script>
  </body>
</html>

이 예제를 실행하면 아래와 같은 결과 화면이 나타납니다.

 
전역 컴포넌트가 등록되었습니다.

이 코드가 실행되어 화면에 나타나기까지의 과정을 살펴보면 다음과 같습니다. 앞에서 살펴본 인스턴스가 화면에 적용되는 과정의 그림에 컴포넌트 등록 부분과 변환 부분을 추가하였습니다.

이 그림을 보면 인스턴스가 생성되고, 인스턴스 내용이 화면 요소로 변환될 때 컴포넌트 태그도 함께 변환됩니다. 따라서 컴포넌트 태그에 정의한 컴포넌트 내용은 사용자가 볼 수 있는 형태의 화면 요소로 최종 변환됩니다.

전연 컴포넌트를 등록하려면 HTML에서 사용할 태그 이름을 컴포넌트 이름으로 작성하고, 중괄호 { } 안에는 HTML 태그가 실제로 화면에서 그려질 때 표시될 내용(컴포넌트 내용)을 작성해야 합니다. 이 예제에서는 컴포넌트의 이름을 my-component로 지정했고, 컴포넌트 내용으로는 template 속성을 정의하고 '전역 컴포넌트가 등록되었습니다.'라는 <div> 태그를 작성했습니다.

따라서 이 컴포넌트를 아래와 같이 HTML에 추가하면 최종적으로 컴포넌트가 등록됩니다.

<my-component> </my-component>

그리고 등록된 my-component 컴포넌트는 실제로 화면에서 아래와 같이 그려집니다.

<div>전역 컴포넌트가 등록되었습니다.</div>

결론적으로 인스턴스가 생성된 후 화면에 그려질 때 실제 HTML 코드 모양은 다음과 같습니다.

   <div id="app">
       <button>컴포넌트 등록</button>
       <!-- 등록한 my-component가 최종적으로 변환된 모습-->
       <div>전역 컴포넌트가 등록되었습니다.</div>
    </div>

지역 컴포넌트 등록

지역 컴포넌트 등록은 전역 컴포넌트 등록과는 다르게 인스턴스에 component속성을 추가하고 등록할 컴포넌트 이름과 내용을 정의하면 됩니다. 지역 컴포넌트 등록 형식은 아래와 같습니다.

new Vue({
   component :
 {
       '컴포넌트 이름' : '컴포넌트 내용'
   }
});

컴포넌트 이름은 전역 컴포넌트와 마찬가지로 HTML에 등록할 사용자 정의 태그를 의미하고, 컴포넌트 내용은 컴포넌트 태그가 실제 화면 요소로 변환될 때의 내용을 의미합니다.

그럼 지역 컴포넌트를 등록하는 방법을 다음 예제를 통해 살펴보겠습니다.

<script>
   var cmp = {
      // 컴포넌트 내용
      template : '<div>지역 컴포넌트가 등록되었습니다.</div>'
   };

   new Vue ({
      el : '#app',
      component : {
           'my-local-component' : cmp  
      }
   });
</script>

변수 cmp에는 홤ㄴ에 나타낼 컴포넌트의 내용을 정의했습니다. 컴포넌트의 내용에 template, data, methods 등 여러 가지 속성이 들어갈 수 있지만 여기서는 간단히 컴포넌트를 등록하는 코드만 보여주기 위해 template 속성만 사용하였습니다. 그리고 template 속성에 <div> 태그 1개만 설정합니다. 아래 뷰 인스턴스에 component 속성을 추가하고 컴포넌트 이름에는 my-local-component를, 컴포넌트 내용에는 앞에서 컴포넌트 내용을 정의한 변수 cmp를 지정합니다.

그리고 HTML에 <my-local-component> 태그를 추가하여 컴포넌트를 화면에 나타냅니다.

   <div id="app">
       <button>컴포넌트 등록</button>
       <my-local-component> </my-local-component>
    </div>

이 예제를 실행하면 아래와 같은 결과 화면이 나타납니다.

  
   지역 컴포넌트가 등록되었습니다.    

지금까지 지역 컴포넌트 등록과 전역 컴포넌트 등록에 대하여 알아보았습니다. 그런데 아직 웹 페이지 상에서 드러나는 결과만으로는 전역 컴포넌트와 지역 컴포넌트의 차이점을 찾기가 어렵습니다. 앞에서 배운 인스턴스 유효 범위를 이용해서 전역 컴포넌트, 지역 컴포넌트 간에는 어떤 차이점이 있는지 살펴보겠습니다.

#3. 지역 컴포넌트와 전역 컴포넌트의 차이


지역 컴포넌트와 전역 컴포넌트의 차이점을 이해하기 위해서는 앞에서 배운 인스턴스의 유효 범위를 이해해야 합니다. 인스턴스의 유효 범위란 HTML의 특정 범위 안에서만 인스턴스의 내용이 유효한 것이라고 했는데, 그럼 다음 코드를 살펴보겠습니다.

   <div id="app">
       <h3>첫 번째 인스턴스 영역</h3>
       <my-global-component> </my-global-component>   -------------- 전역 컴포넌트           
       <my-local-component> </my-local-component>        -------------- 지역 컴포넌트
   </div>

    <script>
        // 전역 컴포넌트 등록
        Vue.component('my-global-component' , {
             template : '<div>전역 컴포넌트입니다.</div>'
         });
        // 지역 컴포넌트 내용
        var cmp = {
             template : '<div>지역 컴포넌트입니다.</div>'
         });
        // 뷰 인스턴스 생성
        new Vue ({
           el : '#app',
           // 지역 컴포넌트 등록
           component : {
               'my-local-component' : cmp
           }
        });
    </script>     

위 코드는 인스턴스를 하나 생성하여 my-global-component 전역 컴포넌트와 my-local-component 지역 컴포넌트를 등록하고 화면에 나타내는 예제입니다. 코드를 실행하면 아래와 같은 결과 화면이 나타납니다.

첫 번째 인스턴스 영역


전역 컴포넌트입니다.
지역 컴포넌트 입니다.

여기까지 앞에서 배운 내용으로 충분히 이해할 수 있을 겁니다. 그럼 이번에는 인스턴스를 하나 더 생성하고 해당 인스턴스에서 지역, 전역 컴포넌트를 모두 표시해 보겠습니다.

  <div id="app">
       <h3>첫 번째 인스턴스 영역</h3>
       <my-global-component> </my-global-component>   -------------- 전역 컴포넌트           
       <my-local-component> </my-local-component>        -------------- 지역 컴포넌트
  </div>
  <hr>
  </div id="app2">
       <h3>두 번째 인스턴스 영역</h3>
       <my-global-component> </my-global-component>   -------------- 전역 컴포넌트           
       <my-local-component> </my-local-component>        -------------- 지역 컴포넌트
  </div>
  
  
    <script>
        // 전역 컴포넌트 등록
        Vue.component('my-global-component' , {
             template : '<div>전역 컴포넌트입니다.</div>'
         });
        // 지역 컴포넌트 내용
        var cmp = {
             template : '<div>지역 컴포넌트입니다.</div>'
         });
        // 첫번째 인스턴스 생성
        new Vue ({
           el : '#app',
           // 지역 컴포넌트 등록
           component : {
               'my-local-component' : cmp
           }
        });
         // 두번째 인스턴스 생성
        new Vue ({
           el : '#app2'
        });
    </script>     

HTML에 <div id="app2"> 태그를 하나 더 추가하고, 인스턴스도 하나 더 추가하였습니다. 그리고 <div id="app2"> 태그에 전역, 지역 컴포넌트를 모두 등록하였습니다. 첫 번째 인스턴스 영역과 두 번째 인스턴스 영역을 구분하기 위해 구분선으로는 <hr> 태그를 사용하였습니다.

첫 번째 인스턴스 영역


전역 컴포넌트입니다.
지역 컴포넌트 입니다.

두 번째 인스턴스 영역


첫 번째

인스턴스 영역에는 전역, 지역 컴포넌트가 모두 정상적으로 나타났습니다. 하지만 구분선 밑에 두 번째 인스턴스 영역에는 전역 컴포넌트만 나타나고, 지역 컴포넌트는 나타나지 않았습니다. 왜 그럴까요? 전역 컴포넌트와 지역 컴포넌트의 유효 범위가 다르기 때문입니다.

전역 컴포넌트는 인스턴스를 새로 생성할 때마다 인스턴스에 component 속성으로 등록할 필요없이 한 번 등록하면 어느 인스턴스에서든지 사용할 수 있습니다. 반대로 지역 컴포넌트는 새 인스턴스를 생성할 때마다 등록해 줘야 합니다.

첫 번째 인스턴스의 유효 범위는 첫 번째 인스턴스 영역으로 제한되기 때문에 <div id="app">에 지역 컴포넌트를 등록했어도 두 번째 인스턴스 영역인 <div id="app2">의 범위 안에서는 지역 컴포넌트가 인식되지 않아 위와 같은 결과를 나타냅니다.

<my-local-component> 태그는 두 번째 인스턴스의 유효 범이 안에 있더라도 이 컴포넌트가 등록된 첫 번째 유효 범위를 벗어나기 때문에 브라우저에서는 HTML 사용자 정의 태그로 인식하고, 뷰에서는 해당 컴포넌트를 제대로 등록했는지 물어보는 오류를 표시합니다.

 

Reference

1. 장기효 [예제로 이해하고 실전 프로젝트로 완성한다! Vue.js 입문]

반응형

'Development > Web' 카테고리의 다른 글

[Web] 크로스 도메인  (0) 2022.09.23
[Web] 뷰 컴포넌트 통신  (0) 2022.09.19
[Web] 뷰 인스턴스 라이프 사이클  (0) 2022.09.18
[Web] 뷰 인스턴스  (0) 2022.09.18
[Web] JSP에서 엑셀 파일로 저장하기  (0) 2020.04.08

+ Recent posts