springboot 통합 rabbitmq
RabbitMQ 는 Erlang 언어 로 개 발 된 오픈 소스 메시지 큐 시스템 을 사용 하여 AMQP 프로 토 콜 을 바탕 으로 이 루어 집 니 다(AMQP 의 주요 특징 은 메시지,큐,경로,신뢰성,안전).Python,Ruby,.NET,Java,JMS,C,PHP,ActionScript,XMPP,STOMP 등 다양한 클 라 이언 트 를 지원 하고 AJAX 를 지원 합 니 다.분포 식 시스템 에 퍼 가기 메 시 지 를 저장 하 는 데 사용 되 며 용이 성,확장 성,높 은 가용성 등에 서 뛰 어 납 니 다.
관련 개념
메시지 대기 열 은 보통 세 가지 개념 이 있 는데 그것 이 바로 메시지(생산자),대기 열,메시지 수신(소비자)이다.RabbitMQ 는 이 기본 개념 위 에 추상 적 인 것 을 더 했 고 메 시 지 를 보 내 는 것 과 대기 열 사이 에 교환 기 를 넣 었 다.이렇게 메 시 지 를 보 내 는 것 은 대기 열 과 직접적인 관계 가 없 이 교환 기 를 통 해 전달 하고 교환 기 회 는 배포 전략 에 따라 메 시 지 를 대기 열 에 전달 합 니 다.
RabbitMQ 의 중요 한 몇 가지 개념:
가상 호스트:RabbitMQ 는 권한 제 어 를 지원 하지만 최소 제어 입 도 는 가상 호스트 입 니 다.하나의 가상 호스트 는 여러 개의 교환기,대기 열,바 인 딩 을 포함 할 수 있다.
교환기:RabbitMQ 배포 기 는 정책 에 따라 관련 대기 열 에 메 시 지 를 배포 합 니 다.
대기 열:캐 시 메시지 의 용기 입 니 다.
귀속:교환기 와 대기 열의 관 계 를 설정 합 니 다.
교환기(교환)
교환기 의 주요 역할 은 해당 하 는 메 시 지 를 받 고 지정 한 대기 열 에 연결 하 는 것 입 니 다.교환 기 는 Direct,topic,headers,Fanout 등 네 가지 유형 이 있 습 니 다.
Direct 는 RabbitMQ 의 기본 교환기 모드 이자 가장 간단 한 모드 입 니 다.즉,메시지 큐 를 만 들 때 BindingKey 를 지정 합 니 다.발송 자가 메 시 지 를 보 낼 때 해당 하 는 키 를 지정 합 니 다.Key 와 메시지 큐 의 BindingKey 가 일치 할 때 메 시 지 는 이 메시지 큐 에 전 송 됩 니 다.
topic 전송 정 보 는 주로 어댑터 에 의 해 이 루어 집 니 다.대기 열 과 교환기 의 연결 은 주로 하나의 모드(어댑터+문자열)에 의 해 이 루어 집 니 다.메 시 지 를 보 낼 때 지정 한 Key 가 이 모드 와 일치 할 때 만 메시지 대기 열 에 전 송 됩 니 다.
headers 도 하나의 규칙 에 따라 일치 합 니 다.메시지 큐 와 교환기 가 연결 되 어 있 을 때 키 쌍 규칙 을 지정 합 니 다.메 시 지 를 보 낼 때 도 키 쌍 규칙 을 지정 합 니 다.두 그룹의 키 가 규칙 에 일치 할 때 메 시 지 는 일치 하 는 메시지 큐 에 보 냅 니 다.
Fanout 은 라디오 형식 으로 모든 대기 열 에 메 시 지 를 보 냅 니 다.key 를 설정 하 더 라 도 무시 합 니 다.
springboot 통합 RabbitMQ
maven 의존 도 추가
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-amqp
org.springframework.boot
spring-boot-starter-test
test
rabbitmq 서비스 설정 추가(application.yml)
#RabbitMQ
spring:
rabbitmq:
host: ****
port: 5672
username: admin
password: tonyzhang
공통 정적 변수
package com.theeternity.rabbitmq.constant;
/**
* @program: ApiBoot
* @description: rabbitMQ
* @author: TheEternity Zhang
* @create: 2019-02-26 14:36
*/
public class RabbitConstant {
//bean name
public static final String DIRECT_QUEUE_BEAN = "direct_queue_bean";
public static final String TOPIC_QUEUE_MESSAGE_BEAN = "topic_queue_message_bean";
public static final String TOPIC_QUEUE_MESSAGES_BEAN = "topic_queue_messages_bean";
public static final String FANOUT_FIRST_MESSAGE_BEAN = "fanout_first_message_bean";
public static final String FANOUT_SECOND_MESSAGE_BEAN = "fanout_second_message_bean";
public static final String FANOUT_THIRD_MESSAGE_BEAN = "fanout_third_message_bean";
//queue
public static final String DIRECT_QUEUE = "direct_queue";
public static final String TOPIC_QUEUE_MESSAGE = "topic.message";
public static final String TOPIC_QUEUE_MESSAGES = "topic.messages";
public static final String FANOUT_FIRST_MESSAGE = "fanout.first";
public static final String FANOUT_SECOND_MESSAGE = "fanout.second";
public static final String FANOUT_THIRD_MESSAGE = "fanout.third";
//exchange
public static final String DIRECT_EXCHANGE = "direct_exchange";
public static final String TOPIC_EXCHANGE = "topic_exchange";
public static final String FANOUT_EXCHANGE = "fanout_exchange";
//routing-key
public static final String DIRECT_KEY = "direct_key";
public static final String TOPIC_QUEUE_MESSAGE_KEY = "topic.message.key";
public static final String TOPIC_QUEUE_MESSAGES_KEY = "topic.messages.key";
public static final String TOPIC_KEY = "topic.#";
}
통합 다 이 렉 트 교환기
사용 할 메시지 대기 열 및 교환 기 를 용기 에 주입 하고 바 인 딩 합 니 다.
package com.theeternity.rabbitmq.config;
import com.theeternity.rabbitmq.constant.RabbitConstant;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @program: ApiBoot
* @description: rabbit
* @author: TheEternity Zhang
* @create: 2019-02-21 16:34
*/
@Configuration
public class RabbitConfig {
/**
* @Description: direct
* @Param: []
* @return: org.springframework.amqp.core.Queue
* @Author: TheEternity Zhang
* @Date: 2019-02-26 14:50
*/
@Bean(name = RabbitConstant.DIRECT_QUEUE_BEAN)
public Queue Queue() {
return new Queue(RabbitConstant.DIRECT_QUEUE);
}
@Bean
public DirectExchange directExchange() {
return new DirectExchange(RabbitConstant.DIRECT_EXCHANGE, true, false);
}
@Bean
Binding bindingDirectExchangeMessage(@Qualifier(RabbitConstant.DIRECT_QUEUE_BEAN) Queue queue,
DirectExchange directExchange) {
return BindingBuilder.bind(queue).to(directExchange).with(RabbitConstant.DIRECT_KEY);
}
}
전송 논리 편집
package com.theeternity.rabbitmq.sender;
import com.theeternity.rabbitmq.constant.RabbitConstant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @program: ApiBoot
* @description:
* @author: TheEternity Zhang
* @create: 2019-02-21 16:37
*/
@Component
@Slf4j
public class DirectSender {
@Autowired
private AmqpTemplate rabbitTemplate;
public void send(T t) {
log.info(" ",t.toString());
this.rabbitTemplate.convertAndSend(RabbitConstant.DIRECT_EXCHANGE,RabbitConstant.DIRECT_KEY, t.toString());
}
}
수신 논리 편집
package com.theeternity.rabbitmq.receiver;
import com.theeternity.rabbitmq.constant.RabbitConstant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* @program: ApiBoot
* @description:
* @author: TheEternity Zhang
* @create: 2019-02-21 16:38
*/
@Component
@Slf4j
public class DirectReceiver {
@RabbitListener(queues = RabbitConstant.DIRECT_QUEUE)
@RabbitHandler
public void process(String hello) {
log.info("Receiver : " + hello);
}
}
통합 Topic 교환기
사용 할 메시지 대기 열 및 교환 기 를 용기 에 주입 하고 바 인 딩 합 니 다.
package com.theeternity.rabbitmq.config;
import com.theeternity.rabbitmq.constant.RabbitConstant;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @program: ApiBoot
* @description: rabbit
* @author: TheEternity Zhang
* @create: 2019-02-21 16:34
*/
@Configuration
public class RabbitConfig {
/**
* @Description: topic
* @Param: []
* @return: org.springframework.amqp.core.Queue
* @Author: TheEternity Zhang
* @Date: 2019-02-26 14:50
*/
@Bean
public Queue queueMessage() {
return new Queue(RabbitConstant.TOPIC_QUEUE_MESSAGE);
}
@Bean
public Queue queueMessages() {
return new Queue(RabbitConstant.TOPIC_QUEUE_MESSAGES);
}
@Bean
public TopicExchange exchange() {
return new TopicExchange(RabbitConstant.TOPIC_EXCHANGE);
}
@Bean
Binding bindingExchangeMessage(Queue queueMessage, TopicExchange exchange) {
return BindingBuilder.bind(queueMessage).to(exchange).with(RabbitConstant.TOPIC_QUEUE_MESSAGE_KEY);
}
@Bean
Binding bindingExchangeMessages(Queue queueMessages, TopicExchange exchange) {
//* ,#
return BindingBuilder.bind(queueMessages).to(exchange).with(RabbitConstant.TOPIC_KEY);
}
}
송신 논리
package com.theeternity.rabbitmq.sender;
import com.theeternity.rabbitmq.constant.RabbitConstant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @program: ApiBoot
* @description: topic
* @author: TheEternity Zhang
* @create: 2019-02-25 13:46
*/
@Component
@Slf4j
public class TopicSender {
@Autowired
private AmqpTemplate rabbitTemplate;
public void send() {
log.info("topic-message ");
rabbitTemplate.convertAndSend(RabbitConstant.TOPIC_EXCHANGE,RabbitConstant.TOPIC_QUEUE_MESSAGE_KEY,"hello,rabbit");
}
public void send1() {
log.info("topic-messages ");
rabbitTemplate.convertAndSend(RabbitConstant.TOPIC_EXCHANGE,RabbitConstant.TOPIC_QUEUE_MESSAGES_KEY,"hello,rabbits");
}
}
수신 감청 논리
package com.theeternity.rabbitmq.receiver;
import com.theeternity.rabbitmq.constant.RabbitConstant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* @program: ApiBoot
* @description: topic
* @author: TheEternity Zhang
* @create: 2019-02-25 13:47
*/
@Component
@Slf4j
public class TopicReceiver {
// Queue
@RabbitListener(queues= RabbitConstant.TOPIC_QUEUE_MESSAGE)
@RabbitHandler
public void process1(String str) {
log.info("message:"+str);
}
// Queue
@RabbitListener(queues=RabbitConstant.TOPIC_QUEUE_MESSAGES)
@RabbitHandler
public void process2(String str) {
log.info("messages:"+str);
}
}
++==지식 포인트=++:
TopicExchange *、#
* 。
# 。
비고:
topic 교환기 위의 방법 은 queue 위 에 bean 의 별명 을 설정 하지 않 았 습 니 다.설정 되 지 않 은 경우 바 인 딩 할 때 들 어 오 는 queue 이름 은 대응 하 는 queue 방법 이름과 같 아야 합 니 다.불필요 한 번 거 로 움 을 초래 하기 위해 서 는 queue 에 별명 을 직접 설정 한 후 바 인 딩 할 때@Qualifier 를 사용 하여 이름 에 따라 주입 하 는 것 을 권장 합 니 다.다음 과 같 습 니 다.
/**
* @Description: topic
* @Param: []
* @return: org.springframework.amqp.core.Queue
* @Author: TheEternity Zhang
* @Date: 2019-02-26 14:50
*/
@Bean(name = RabbitConstant.TOPIC_QUEUE_MESSAGE_BEAN)
public Queue queueMessage() {
return new Queue(RabbitConstant.TOPIC_QUEUE_MESSAGE);
}
@Bean(name = RabbitConstant.TOPIC_QUEUE_MESSAGES_BEAN)
public Queue queueMessages() {
return new Queue(RabbitConstant.TOPIC_QUEUE_MESSAGES);
}
@Bean
public TopicExchange exchange() {
return new TopicExchange(RabbitConstant.TOPIC_EXCHANGE);
}
@Bean
Binding bindingExchangeMessage(@Qualifier(RabbitConstant.TOPIC_QUEUE_MESSAGE_BEAN) Queue queueMessage, TopicExchange exchange) {
return BindingBuilder.bind(queueMessage).to(exchange).with(RabbitConstant.TOPIC_QUEUE_MESSAGE_KEY);
}
@Bean
Binding bindingExchangeMessages(@Qualifier(RabbitConstant.TOPIC_QUEUE_MESSAGES_BEAN) Queue queueMessages, TopicExchange exchange) {
//* ,#
return BindingBuilder.bind(queueMessages).to(exchange).with(RabbitConstant.TOPIC_KEY);
}
통합 Fanout 교환기
사용 할 메시지 대기 열 및 교환 기 를 용기 에 주입 하고 바 인 딩 합 니 다.
package com.theeternity.rabbitmq.config;
import com.theeternity.rabbitmq.constant.RabbitConstant;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @program: ApiBoot
* @description: rabbit
* @author: TheEternity Zhang
* @create: 2019-02-21 16:34
*/
@Configuration
public class RabbitConfig {
/**
* @Description: fanout , routingkey
* @Param: []
* @return: org.springframework.amqp.core.Queue
* @Author: TheEternity Zhang
* @Date: 2019-02-26 14:50
*/
@Bean(name = RabbitConstant.FANOUT_FIRST_MESSAGE_BEAN)
public Queue firstMessage() {
return new Queue(RabbitConstant.FANOUT_FIRST_MESSAGE);
}
@Bean(name = RabbitConstant.FANOUT_SECOND_MESSAGE_BEAN)
public Queue secondMessage() {
return new Queue(RabbitConstant.FANOUT_SECOND_MESSAGE);
}
@Bean(name = RabbitConstant.FANOUT_THIRD_MESSAGE_BEAN)
public Queue thirdMessage() {
return new Queue(RabbitConstant.FANOUT_THIRD_MESSAGE);
}
@Bean
FanoutExchange fanoutExchange() {
//
return new FanoutExchange(RabbitConstant.FANOUT_EXCHANGE);
}
@Bean
Binding bindingExchangeA(@Qualifier(RabbitConstant.FANOUT_FIRST_MESSAGE_BEAN) Queue firstMessage, FanoutExchange fanoutExchange) {
return BindingBuilder.bind(firstMessage).to(fanoutExchange);
}
@Bean
Binding bindingExchangeB(@Qualifier(RabbitConstant.FANOUT_SECOND_MESSAGE_BEAN) Queue secondMessage, FanoutExchange fanoutExchange) {
return BindingBuilder.bind(secondMessage).to(fanoutExchange);
}
@Bean
Binding bindingExchangeC(@Qualifier(RabbitConstant.FANOUT_THIRD_MESSAGE_BEAN) Queue thirdMessage, FanoutExchange fanoutExchange) {
return BindingBuilder.bind(thirdMessage).to(fanoutExchange);
}
}
송신 논리
package com.theeternity.rabbitmq.sender;
import com.theeternity.rabbitmq.constant.RabbitConstant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @program: ApiBoot
* @description: Fanout
* @author: TheEternity Zhang
* @create: 2019-02-25 13:47
*/
@Component
@Slf4j
public class FanoutSender {
@Autowired
private AmqpTemplate rabbitTemplate;
public void send() {
log.info("fanout-message ");
rabbitTemplate.convertAndSend(RabbitConstant.FANOUT_EXCHANGE,"","hello,rabbit");
}
}
수신 감청 논리
package com.theeternity.rabbitmq.receiver;
import com.theeternity.rabbitmq.constant.RabbitConstant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* @program: ApiBoot
* @description: Fanout
* @author: TheEternity Zhang
* @create: 2019-02-25 13:48
*/
@Component
@Slf4j
public class FanoutReceiver {
@RabbitListener(queues = RabbitConstant.FANOUT_FIRST_MESSAGE)
public void processA(String str) {
log.info("firstReceive:" + str);
}
@RabbitListener(queues = RabbitConstant.FANOUT_SECOND_MESSAGE)
public void processB(String str) {
log.info("secondReceive:" + str);
}
@RabbitListener(queues = RabbitConstant.FANOUT_THIRD_MESSAGE)
public void processC(String str) {
log.info("thirdReceive:" + str);
}
}
비고:
실제로 RabbitMQ 는 전송 대상 도 지원 할 수 있 습 니 다.물론 직렬 화 와 반 직렬 화 와 관련 되 기 때문에 이 대상 은 Serializable 인 터 페 이 스 를 실현 해 야 합 니 다.예 를 들 어:
package com.theeternity.core.AutoGenerator.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import java.time.LocalDateTime;
import java.io.Serializable;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @author TheEternity Zhang
* @since 2019-02-01
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("we_user")
public class WeUserEntity implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "user_id", type = IdType.AUTO)
private Integer userId;
private String userName;
private String password;
private LocalDateTime createdTime;
private LocalDateTime updateTime;
}
프로필 전체:
package com.theeternity.rabbitmq.config;
import com.theeternity.rabbitmq.constant.RabbitConstant;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @program: ApiBoot
* @description: rabbit
* @author: TheEternity Zhang
* @create: 2019-02-21 16:34
*/
@Configuration
public class RabbitConfig {
/**
* @Description: direct
* @Param: []
* @return: org.springframework.amqp.core.Queue
* @Author: TheEternity Zhang
* @Date: 2019-02-26 14:50
*/
@Bean(name = RabbitConstant.DIRECT_QUEUE_BEAN)
public Queue Queue() {
return new Queue(RabbitConstant.DIRECT_QUEUE);
}
@Bean
public DirectExchange directExchange() {
return new DirectExchange(RabbitConstant.DIRECT_EXCHANGE, true, false);
}
@Bean
Binding bindingDirectExchangeMessage(@Qualifier(RabbitConstant.DIRECT_QUEUE_BEAN) Queue queue,
DirectExchange directExchange) {
return BindingBuilder.bind(queue).to(directExchange).with(RabbitConstant.DIRECT_KEY);
}
/**
* @Description: topic
* @Param: []
* @return: org.springframework.amqp.core.Queue
* @Author: TheEternity Zhang
* @Date: 2019-02-26 14:50
*/
@Bean(name = RabbitConstant.TOPIC_QUEUE_MESSAGE_BEAN)
public Queue queueMessage() {
return new Queue(RabbitConstant.TOPIC_QUEUE_MESSAGE);
}
@Bean(name = RabbitConstant.TOPIC_QUEUE_MESSAGES_BEAN)
public Queue queueMessages() {
return new Queue(RabbitConstant.TOPIC_QUEUE_MESSAGES);
}
@Bean
public TopicExchange exchange() {
return new TopicExchange(RabbitConstant.TOPIC_EXCHANGE);
}
@Bean
Binding bindingExchangeMessage(@Qualifier(RabbitConstant.TOPIC_QUEUE_MESSAGE_BEAN) Queue queueMessage, TopicExchange exchange) {
return BindingBuilder.bind(queueMessage).to(exchange).with(RabbitConstant.TOPIC_QUEUE_MESSAGE_KEY);
}
@Bean
Binding bindingExchangeMessages(@Qualifier(RabbitConstant.TOPIC_QUEUE_MESSAGES_BEAN) Queue queueMessages, TopicExchange exchange) {
//* ,#
return BindingBuilder.bind(queueMessages).to(exchange).with(RabbitConstant.TOPIC_KEY);
}
/**
* @Description: fanout , routingkey
* @Param: []
* @return: org.springframework.amqp.core.Queue
* @Author: TheEternity Zhang
* @Date: 2019-02-26 14:50
*/
@Bean(name = RabbitConstant.FANOUT_FIRST_MESSAGE_BEAN)
public Queue firstMessage() {
return new Queue(RabbitConstant.FANOUT_FIRST_MESSAGE);
}
@Bean(name = RabbitConstant.FANOUT_SECOND_MESSAGE_BEAN)
public Queue secondMessage() {
return new Queue(RabbitConstant.FANOUT_SECOND_MESSAGE);
}
@Bean(name = RabbitConstant.FANOUT_THIRD_MESSAGE_BEAN)
public Queue thirdMessage() {
return new Queue(RabbitConstant.FANOUT_THIRD_MESSAGE);
}
@Bean
FanoutExchange fanoutExchange() {
//
return new FanoutExchange(RabbitConstant.FANOUT_EXCHANGE);
}
@Bean
Binding bindingExchangeA(@Qualifier(RabbitConstant.FANOUT_FIRST_MESSAGE_BEAN) Queue firstMessage, FanoutExchange fanoutExchange) {
return BindingBuilder.bind(firstMessage).to(fanoutExchange);
}
@Bean
Binding bindingExchangeB(@Qualifier(RabbitConstant.FANOUT_SECOND_MESSAGE_BEAN) Queue secondMessage, FanoutExchange fanoutExchange) {
return BindingBuilder.bind(secondMessage).to(fanoutExchange);
}
@Bean
Binding bindingExchangeC(@Qualifier(RabbitConstant.FANOUT_THIRD_MESSAGE_BEAN) Queue thirdMessage, FanoutExchange fanoutExchange) {
return BindingBuilder.bind(thirdMessage).to(fanoutExchange);
}
}
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.