메일
메일 (Mail)
Apache Camel의 Mail 컴포넌트는 Spring의 Mail 지원과 JavaMail을 기반으로 이메일을 주고받게 해 주는 컴포넌트예요. SMTP, POP3, IMAP 프로토콜과 각각의 보안 변형(smtps, pop3s, imaps)을 지원하고, Camel 1.0부터 producer와 consumer를 모두 제공해요.
출처: 문서
본문
Camel 1.0부터 지원
producer와 consumer 모두 지원해요
Mail 컴포넌트는 Spring의 Mail 지원과 그 밑바탕인 JavaMail 시스템을 통해 이메일에 접근하게 해 줘요.
Maven 사용자라면 이 컴포넌트를 위해 다음 의존성을 pom.xml에 추가해야 해요:
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-mail</artifactId>
<version>x.x.x</version>
<!-- use the same version as your Camel core version -->
</dependency>
팁 POP3와 IMAP
POP3에는 몇 가지 한계가 있어서, 가능하면 IMAP을 쓰도록 최종 사용자에게 권해요.
참고 테스트용 목 메일 사용 (Using mock-mail for testing)
모킹 프레임워크를 쓰면 실제 메일 서버 없이도 단위 테스트를 할 수 있어요. 다만 프로덕션이나 실제 메일 서버로 메일을 보내야 하는 다른 환경으로 갈 때는 mock-mail을 포함하지 않는 것을 잊지 마세요. classpath에 mock-javamail.jar가 있기만 해도 그게 작동을 시작해서 메일 전송을 막아 버려요.
URI 형식 (URI format)
메일 엔드포인트는 다음 URI 형식 중 하나를 가져요(각각 SMTP, POP3, IMAP 프로토콜용이에요):
smtp://[username@]host[:port][?options] pop3://[username@]host[:port][?options] imap://[username@]host[:port][?options]
mail 컴포넌트는 이 프로토콜들의 보안 변형(SSL 위에 겹쳐 쓰는 방식)도 지원해요. 스킴에 s를 붙이면 보안 프로토콜을 켤 수 있어요:
smtps://[username@]host[:port][?options] pop3s://[username@]host[:port][?options] imaps://[username@]host[:port][?options]
옵션 설정 방법 (Configuring Options)
Camel 컴포넌트는 두 단계로 나눠 설정합니다:
-
component level
-
endpoint level
컴포넌트 옵션 설정 (Configuring Component Options)
컴포넌트 레벨에서는 엔드포인트들이 이어서 물려받는 일반적이고 공유되는 설정을 해요. 가장 상위의 설정 레벨이죠.
예를 들어 컴포넌트에는 보안 설정, 인증용 자격 증명, 네트워크 연결 URL 같은 것들이 있을 수 있어요.
컴포넌트에 따라 옵션이 몇 개뿐인 것도 있고 아주 많은 것도 있어요. 컴포넌트는 보통 널리 쓰이는 기본값이 미리 설정되어 있어서, 몇 가지 옵션만 설정하거나 아예 하나도 건드리지 않아도 되는 경우가 많아요.
컴포넌트는 다음 방법으로 설정할 수 있어요:
-
the Component DSL.
-
in a configuration file (
application.properties,*.yamlfiles, etc). -
directly in the Java code.
엔드포인트 옵션 설정 (Configuring Endpoint Options)
엔드포인트는 옵션이 많아서 설정에 시간을 더 쓰게 될 거예요. 이 옵션들이 엔드포인트가 원하는 일을 하도록 커스터마이즈해 줘요. 옵션은 엔드포인트가 consumer(from)로 쓰이는지, producer(to)로 쓰이는지, 둘 다인지에 따라 분류되기도 해요.
엔드포인트 설정은 대부분 엔드포인트 URI에 _path_와 query 파라미터로 바로 하는 경우가 많아요. Java에서 엔드포인트와 데이터 포맷을 _타입 세이프_하게 설정하려면 Endpoint DSL과 DataFormat DSL을 쓸 수도 있어요.
옵션을 설정할 때 좋은 습관은 Property Placeholders를 사용하는 거예요.
프로퍼티 플레이스홀더는 이런 장점이 있어요:
-
They help prevent using hardcoded urls, port numbers, sensitive information, and other settings.
-
They allow externalizing the configuration from the code.
-
They help the code to become more flexible and reusable.
다음 두 섹션에서 모든 옵션을 나열해요. 먼저 컴포넌트 옵션, 그다음 엔드포인트 옵션 순서예요.
컴포넌트 옵션 (Component Options)
Mail 컴포넌트가 지원하는 옵션은 아래와 같아요.
| Name | Description | Default | Type |
|---|---|---|---|
| bridgeErrorHandler (consumer) | 컨슈머를 Camel 라우팅 에러 핸들러(Error Handler)에 브리지할 수 있게 해 줘요. 즉, Camel 컨슈머가 들어오는 메시지를 집어 올리는 동안 등에서 예외가 발생하면(가능한 경우), 그 예외를 메시지로 처리해서 라우팅 에러 핸들러가 다루게 돼요. 중요: 이것은 서드파티 컴포넌트가 예외가 던져졌을 때 Camel에 알릴 수 있도록 허용하는 경우에만 가능해요. 일부 컴포넌트는 내부에서만 처리하기 때문에 bridgeErrorHandler를 쓸 수 없어요. 다른 상황에서는 Camel 컴포넌트를 개선해서 서드파티 컴포넌트에 연결(hook)해 이를 가능하게 만들 수도 있어요. 기본으로 컨슈머는 org.apache.camel.spi.ExceptionHandler로 예외를 처리하는데, WARN 또는 ERROR 레벨로 로그를 남기고 무시해요. | false | boolean |
| closeFolder (consumer) | 폴링 후 consumer가 폴더를 닫을지 여부예요. 이 옵션을 false로 두고 disconnect=false로까지 두면, consumer가 폴링 사이에 폴더를 열어 둔 채로 유지해요. | true | boolean |
| copyTo (consumer) | 메일 메시지를 처리한 후, 지정한 이름의 메일 폴더로 복사할 수 있어요. 이 설정값은 copyTo 키를 가진 헤더로 덮어쓸 수 있어서, 런타임에 설정한 폴더 이름으로 메시지를 복사할 수 있어요. | String | |
| decodeFilename (consumer) | true로 설정하면 파일 이름을 디코딩할 때 MimeUtility.decodeText 메서드를 사용해요. JVM 시스템 프로퍼티 mail.mime.encodefilename을 설정하는 것과 비슷해요. | false | boolean |
| delete (consumer) | 메시지를 처리한 후 삭제해요. 메일 메시지에 DELETED 플래그를 설정하는 방식으로 이뤄져요. false면 대신 SEEN 플래그가 설정돼요. 메일을 삭제할지 여부는 delete 키를 가진 헤더를 설정해서 이 옵션을 덮어쓸 수 있어요. | false | boolean |
| disconnect (consumer) | 폴링 후 consumer가 연결을 끊을지 여부예요. 켜면 매 폴링마다 Camel이 강제로 다시 연결해요. | false | boolean |
| handleFailedMessage (consumer) | 메일 컨슈머가 특정 메일 메시지를 가져오지 못했을 때, 발생한 예외를 consumer의 에러 핸들러로 처리하게 해 주는 옵션이에요. consumer에서 bridge error handler를 켜면 대신 Camel 라우팅 에러 핸들러가 예외를 처리할 수 있어요. 기본 동작은 컨슈머가 예외를 던지고, 배치에 담긴 어떤 메일도 Camel이 라우팅하지 못하게 되는 거예요. | false | boolean |
| mimeDecodeHeaders (consumer) | 메일 헤더에 대해 투명한 MIME 디코딩과 unfolding을 켜는 옵션이에요. | false | boolean |
| moveTo (consumer) | 메일 메시지를 처리한 후, 지정한 이름의 메일 폴더로 이동할 수 있어요. 이 설정값은 moveTo 키를 가진 헤더로 덮어쓸 수 있어서, 런타임에 설정한 폴더 이름으로 메시지를 이동할 수 있어요. | String | |
| peek (consumer) | 메일 메시지를 처리하기 전에 jakarta.mail.Message를 peeked로 표시해요. 이 옵션은 IMAPMessage 메시지 타입에만 적용돼요. peek를 쓰면 메일이 메일 서버에서 성급하게 SEEN으로 표시되지 않아서, Camel에서 처리 오류가 나면 메일 메시지를 되돌릴 수 있어요. | true | boolean |
| skipFailedMessage (consumer) | 메일 컨슈머가 특정 메일 메시지를 가져오지 못했을 때, 그 메시지를 건너뛰고 다음 메일 메시지를 가져오러 진행하게 해 주는 옵션이에요. 기본 동작은 컨슈머가 예외를 던지고, 배치에 담긴 어떤 메일도 Camel이 라우팅하지 못하게 되는 거예요. | false | boolean |
| unseen (consumer) | 읽지 않은(unseen) 메일만으로 범위를 제한할지 여부예요. | true | boolean |
| failOnDuplicateFileAttachment (consumer (advanced)) | 메일 메시지에 파일 이름이 중복된 첨부 파일이 있을 때 메일 처리를 실패로 만들지 여부예요. false로 두면 중복 첨부는 건너뛰고 WARN 로그를 남겨요. true로 두면 예외를 던져서 메일 메시지 처리가 실패해요. | false | boolean |
| fetchSize (consumer (advanced)) | 한 번의 폴링에서 소비할 메시지 수의 최댓값을 정해요. 메일박스 폴더에 메시지가 많을 때 메일 서버에 과부하가 걸리는 걸 막는 데 쓸 수 있어요. 기본값 -1은 fetch size가 없다는 뜻이고 모든 메시지를 소비해요. 0으로 설정하면 특수한 코너 케이스로, Camel이 메시지를 전혀 소비하지 않아요. | -1 | int |
| folderName (consumer (advanced)) | 폴링할 폴더예요. | INBOX | String |
| generateMissingAttachmentNames (consumer (advanced)) | 첨부 파일에 파일 이름이 없을 때 'uuid'로 설정하면 파일 이름에 UUID를 붙여 줘요. | String | |
| handleDuplicateAttachmentNames (consumer (advanced)) | 첨부 파일 이름이 중복될 때의 처리 전략을 정해요. never: 첨부 목록에 이미 있는 파일 이름을 가진 첨부는 failOnDuplicateFileAttachment가 true로 설정돼 있지 않은 한 무시돼요. uuidPrefix: 중복 첨부 파일 이름 앞에 각각 uuid와 언더스코어를 붙여요(uuid_filename.fileextension). uuidSuffix: 중복 첨부 파일 이름 뒤에 언더스코어와 uuid를 붙여요(filename_uuid.fileextension). | String | |
| mapMailMessage (consumer (advanced)) | 받은 메일 메시지를 Camel body/headers/attachments로 매핑할지 여부를 정해요. true로 설정하면 메일 메시지 본문이 Camel IN 메시지의 본문으로, 메일 헤더는 IN 헤더로, 첨부는 Camel IN 첨부 메시지로 매핑돼요. 이 옵션을 false로 설정하면 IN 메시지에 raw jakarta.mail.Message가 담겨요. 이 raw 메시지는 exchange.getIn().getBody(jakarta.mail.Message.class)를 호출해서 꺼낼 수 있어요. | true | boolean |
| bcc (producer) | BCC 이메일 주소를 설정해요. 여러 이메일 주소는 쉼표로 구분하세요. | String | |
| cc (producer) | CC 이메일 주소를 설정해요. 여러 이메일 주소는 쉼표로 구분하세요. | String | |
| from (producer) | 보내는 사람(from) 이메일 주소예요. | camel@localhost | String |
| lazyStartProducer (producer) | 프로듀서를 느리게(lazy, 첫 메시지가 올 때) 시작할지 여부예요. 느리게 시작하면, 프로듀서가 시작 중에 실패해서 라우트 시작 자체가 실패하는 상황에서도 CamelContext와 라우트를 시작할 수 있어요. 시작을 늦춰 두면 실패가 발생했을 때 메시지를 라우팅하는 동안 Camel의 라우팅 에러 핸들러로 처리할 수 있죠. 단, 첫 메시지를 처리할 때 프로듀서를 만들고 시작하는 데 시간이 조금 걸려서 전체 처리 시간이 길어질 수 있다는 점은 감안하세요. | false | boolean |
| replyTo (producer) | Reply-To 수신자(응답 메일을 받을 사람)예요. 여러 이메일 주소는 쉼표로 구분하세요. | String | |
| subject (producer) | 보내는 메시지의 제목(Subject)이에요. 참고: 헤더에서 제목을 설정하면 이 옵션보다 우선해요. | String | |
| to (producer) | 받는(목적지) 이메일 주소를 설정해요. 여러 이메일 주소는 쉼표로 구분하세요. | String | |
| useHeaderFrom (producer) | 메시지 헤더 From과 Sender가 엔드포인트 URI에 미리 설정된 보내는 사람을 덮어쓸지 여부예요. 기본값은 false예요. true로 두면 메시지 헤더 From과 Sender가 엔드포인트 URI의 보내는 사람을 덮어써요. | false | boolean |
| useHeaderRecipients (producer) | 메시지 헤더 To, CC, BCC가 엔드포인트 URI에 미리 설정된 수신자를 덮어쓸지 여부예요. 기본값은 false예요. true로 두면 메시지 헤더가 엔드포인트 URI의 수신자를 덮어써요. | false | boolean |
| useHeaderReplyTo (producer) | 메시지 헤더 Reply-To가 엔드포인트 URI에 미리 설정된 replyTo를 덮어쓸지 여부예요. 기본값은 false예요. true로 두면 메시지 Reply-To 헤더가 엔드포인트 URI의 replyTo를 덮어써요. | false | boolean |
| useHeaderSubject (producer) | 메시지 헤더 Subject가 엔드포인트 URI에 미리 설정된 subject를 덮어쓸지 여부예요. 기본값은 false예요. true로 두면 메시지 Subject 헤더가 엔드포인트 URI의 subject를 덮어써요. | false | boolean |
| javaMailSender (producer (advanced)) | 이메일을 보낼 때 커스텀 org.apache.camel.component.mail.JavaMailSender를 사용하게 해 줘요. | JavaMailSender | |
| additionalJavaMailProperties (advanced) | 추가 JavaMail 프로퍼티를 설정해요. 다른 모든 옵션으로부터 설정된 기본 프로퍼티에 추가하거나 그걸 덮어써요. 특수한 옵션을 몇 개 추가하면서 나머지는 그대로 두고 싶을 때 유용해요. 접두사가 mail인 다중 값 옵션이에요. | Properties | |
| alternativeBodyHeader (advanced) | 대체 이메일 본문을 담고 있는 IN 메시지 헤더의 키를 지정해요. 예를 들어 text/html 형식으로 이메일을 보내면서 HTML을 지원하지 않는 이메일 클라이언트를 위한 대체 본문을 제공하고 싶다면, 이 키를 헤더로 넣어 대체 메일 본문을 설정하세요. | CamelMailAlternativeBody | String |
| attachmentsContentTransferEncodingResolver (advanced) | 첨부 파일에 어떤 content-type-encoding을 쓸지 결정하는 커스텀 AttachmentsContentTransferEncodingResolver를 사용하게 해 줘요. | AttachmentsContentTransferEncodingResolver | |
| authenticator (advanced) | 로그인에 쓰이는 authenticator예요. 설정하면 password와 username은 무시돼요. 만료될 수 있어서 동적으로 읽어야 하는 토큰에 사용할 수 있어요. | MailAuthenticator | |
| autowiredEnabled (advanced) | 오토와이어링(autowiring)을 활성화할지 여부예요. 레지스트리에서 일치하는 타입의 인스턴스가 딱 하나 있는지 찾아서, 그 인스턴스로 오토와이어링 옵션(옵션이 autowired로 표시되어 있어야 함)을 자동 설정하는 데 쓰여요. JDBC 데이터 소스, JMS 커넥션 팩토리, AWS 클라이언트 등을 자동 설정할 때 사용할 수 있어요. | true | boolean |
| configuration (advanced) | Mail 설정을 지정해요. | MailConfiguration | |
| connectionTimeout (advanced) | 연결 타임아웃(밀리초)이에요. | 30000 | int |
| contentType (advanced) | 메일 메시지의 content type이에요. HTML 메일에는 text/html을 사용하세요. | text/plain | String |
| contentTypeResolver (advanced) | 파일 첨부의 Content-Type을 결정하는 resolver예요. | ContentTypeResolver | |
| debugMode (advanced) | 하위 메일 프레임워크에서 디버그 모드를 켜요. SUN Mail 프레임워크는 기본적으로 디버그 메시지를 System.out에 기록해요. | false | boolean |
| ignoreUnsupportedCharset (advanced) | 메일을 보낼 때 로컬 JVM이 지원하지 않는 charset을 Camel이 무시하게 하는 옵션이에요. charset이 지원되지 않으면 content-type에서 charset=XXX(XXX는 지원되지 않는 charset)를 제거하고, 대신 플랫폼 기본값에 의존해요. | false | boolean |
| ignoreUriScheme (advanced) | 메일을 보낼 때 로컬 JVM이 지원하지 않는 charset을 Camel이 무시하게 하는 옵션이에요. charset이 지원되지 않으면 content-type에서 charset=XXX(XXX는 지원되지 않는 charset)를 제거하고, 대신 플랫폼 기본값에 의존해요. | false | boolean |
| javaMailProperties (advanced) | JavaMail 옵션을 설정해요. 기본 프로퍼티를 모두 지우고 이 메서드로 제공된 프로퍼티만 사용해요. | Properties | |
| maxMultipartDepth (advanced) | 첨부를 추출할 때 컨슈머가 내려가는 multipart MIME 파트의 최대 중첩 깊이예요. 이보다 깊게 중첩된 메시지는 더 깊은 파트를 건너뛰어서, 악의적으로 만든 아주 깊은 multipart가 스택을 소진하지 못하게 해요. 정상적인 소스가 비정상적으로 깊은 중첩을 만들 때만 늘리세요. | 20 | int |
| session (advanced) | 모든 메일 상호작용에 Camel이 사용할 메일 세션을 지정해요. JavaEE 컨테이너처럼 다른 리소스가 메일 세션을 만들고 관리하는 시나리오에서 유용해요. 커스텀 메일 세션을 쓰면 세션에 설정돼 있는 경우 세션의 호스트명과 포트를 사용해요. | Session | |
| useInlineAttachments (advanced) | disposition을 inline으로 쓸지 attachment로 쓸지 여부예요. | false | boolean |
| headerFilterStrategy (filter) | Camel 메시지와 주고받는 헤더를 필터링할 커스텀 org.apache.camel.spi.HeaderFilterStrategy를 사용하게 해 줘요. | HeaderFilterStrategy | |
| healthCheckConsumerEnabled (health) | 이 컴포넌트의 컨슈머 기반 헬스체크를 모두 켜거나 끌 때 사용해요. | true | boolean |
| healthCheckProducerEnabled (health) | 이 컴포넌트의 프로듀서 기반 헬스체크를 모두 켜거나 끌 때 사용해요. 참고: Camel은 기본으로 프로듀서 기반 헬스체크를 모두 꺼두고 있어요. camel.health.producersEnabled=true로 설정하면 프로듀서 체크를 전역으로 켤 수 있어요. | true | boolean |
| password (security) | 로그인 비밀번호예요. setAuthenticator(MailAuthenticator)도 참고하세요. | String | |
| sslContextParameters (security) | SSLContextParameters로 보안을 설정할 때 사용해요. | SSLContextParameters | |
| useGlobalSslContextParameters (security) | 전역 SSL 컨텍스트 파라미터 사용을 켜요. | false | boolean |
| useJavaMailSessionPropertiesFromHeaders (security) | 동적인 JavaMail 세션 프로퍼티(mail.smtp. 또는 mail.smtps.로 시작하는 키의 메시지 헤더)가 메시지별로 엔드포인트 설정을 덮어쓰도록 허용할지 여부예요. 기본으로는 비활성화돼 있어요. 이 헤더가 오직 신뢰할 수 있는 라우트 로직에서 나올 때만 켜고, 신뢰 경계를 넘는 데이터(예: HTTP 쿼리 파라미터, 신뢰할 수 없는 생산자의 JMS/Kafka 메시지)에서 나올 때는 절대 켜지 마세요. 켜 놓으면 이 헤더를 설정할 수 있는 공격자가 전송 보안(mail.smtp.ssl.trust나 mail.smtp.starttls.enable 같은 것)을 약화시키거나 SMTP 연결을 다른 곳으로 돌릴 수 있어요. | false | boolean |
| username (security) | 로그인 사용자 이름이에요. setAuthenticator(MailAuthenticator)도 참고하세요. | String |
엔드포인트 옵션 (Endpoint Options)
Mail 엔드포인트는 다음 URI 구문으로 설정해요:
imap:host:port
다음 _path_와 query 파라미터를 씁니다:
경로 파라미터 (Path Parameters)
| Name | Description | Default | Type |
|---|---|---|---|
| host (common) | Required 메일 서버 호스트 이름이에요. | String | |
| port (common) | 메일 서버의 포트 번호예요. | int |
쿼리 파라미터 (Query Parameters)
| Name | Description | Default | Type |
|---|---|---|---|
| closeFolder (consumer) | 폴링 후 consumer가 폴더를 닫을지 여부예요. 이 옵션을 false로 두고 disconnect=false로까지 두면, consumer가 폴링 사이에 폴더를 열어 둔 채로 유지해요. | true | boolean |
| copyTo (consumer) | 메일 메시지를 처리한 후, 지정한 이름의 메일 폴더로 복사할 수 있어요. 이 설정값은 copyTo 키를 가진 헤더로 덮어쓸 수 있어서, 런타임에 설정한 폴더 이름으로 메시지를 복사할 수 있어요. | String | |
| decodeFilename (consumer) | true로 설정하면 파일 이름을 디코딩할 때 MimeUtility.decodeText 메서드를 사용해요. JVM 시스템 프로퍼티 mail.mime.encodefilename을 설정하는 것과 비슷해요. | false | boolean |
| delete (consumer) | 메시지를 처리한 후 삭제해요. 메일 메시지에 DELETED 플래그를 설정하는 방식으로 이뤄져요. false면 대신 SEEN 플래그가 설정돼요. 메일을 삭제할지 여부는 delete 키를 가진 헤더를 설정해서 이 옵션을 덮어쓸 수 있어요. | false | boolean |
| disconnect (consumer) | 폴링 후 consumer가 연결을 끊을지 여부예요. 켜면 매 폴링마다 Camel이 강제로 다시 연결해요. | false | boolean |
| handleFailedMessage (consumer) | 메일 컨슈머가 특정 메일 메시지를 가져오지 못했을 때, 발생한 예외를 consumer의 에러 핸들러로 처리하게 해 주는 옵션이에요. consumer에서 bridge error handler를 켜면 대신 Camel 라우팅 에러 핸들러가 예외를 처리할 수 있어요. 기본 동작은 컨슈머가 예외를 던지고, 배치에 담긴 어떤 메일도 Camel이 라우팅하지 못하게 되는 거예요. | false | boolean |
| maxMessagesPerPoll (consumer) | 폴링마다 모아 올 메시지 수의 최댓값을 지정해요. 기본으로는 최댓값이 정해져 있지 않아요. 서버가 시작할 때 수천 개의 파일을 내려받는 걸 막으려고 예를 들어 1000 같은 한도를 거는 데 쓸 수 있어요. 0이나 음수를 넣으면 이 옵션을 끌 수 있어요. | int | |
| mimeDecodeHeaders (consumer) | 메일 헤더에 대해 투명한 MIME 디코딩과 unfolding을 켜는 옵션이에요. | false | boolean |
| moveTo (consumer) | 메일 메시지를 처리한 후, 지정한 이름의 메일 폴더로 이동할 수 있어요. 이 설정값은 moveTo 키를 가진 헤더로 덮어쓸 수 있어서, 런타임에 설정한 폴더 이름으로 메시지를 이동할 수 있어요. | String | |
| peek (consumer) | 메일 메시지를 처리하기 전에 jakarta.mail.Message를 peeked로 표시해요. 이 옵션은 IMAPMessage 메시지 타입에만 적용돼요. peek를 쓰면 메일이 메일 서버에서 성급하게 SEEN으로 표시되지 않아서, Camel에서 처리 오류가 나면 메일 메시지를 되돌릴 수 있어요. | true | boolean |
| sendEmptyMessageWhenIdle (consumer) | 폴링 컨슈머가 파일을 하나도 가져오지 못했을 때, 이 옵션을 켜면 대신 빈 메시지(본문 없음)를 보내 줘요. | false | boolean |
| skipFailedMessage (consumer) | 메일 컨슈머가 특정 메일 메시지를 가져오지 못했을 때, 그 메시지를 건너뛰고 다음 메일 메시지를 가져오러 진행하게 해 주는 옵션이에요. 기본 동작은 컨슈머가 예외를 던지고, 배치에 담긴 어떤 메일도 Camel이 라우팅하지 못하게 되는 거예요. | false | boolean |
| unseen (consumer) | 읽지 않은(unseen) 메일만으로 범위를 제한할지 여부예요. | true | boolean |
| bridgeErrorHandler (consumer (advanced)) | 컨슈머를 Camel 라우팅 에러 핸들러(Error Handler)에 브리지할 수 있게 해 줘요. 즉, Camel 컨슈머가 들어오는 메시지를 집어 올리는 동안 등에서 예외가 발생하면(가능한 경우), 그 예외를 메시지로 처리해서 라우팅 에러 핸들러가 다루게 돼요. 중요: 이것은 서드파티 컴포넌트가 예외가 던져졌을 때 Camel에 알릴 수 있도록 허용하는 경우에만 가능해요. 일부 컴포넌트는 내부에서만 처리하기 때문에 bridgeErrorHandler를 쓸 수 없어요. 다른 상황에서는 Camel 컴포넌트를 개선해서 서드파티 컴포넌트에 연결(hook)해 이를 가능하게 만들 수도 있어요. 기본으로 컨슈머는 org.apache.camel.spi.ExceptionHandler로 예외를 처리하는데, WARN 또는 ERROR 레벨로 로그를 남기고 무시해요. | false | boolean |
| exceptionHandler (consumer (advanced)) | 컨슈머가 커스텀 ExceptionHandler를 쓰게 할 수 있어요. bridgeErrorHandler 옵션이 활성화되어 있으면 이 옵션은 사용되지 않는다는 점에 주의하세요. 기본으로 컨슈머는 예외를 처리하는데, WARN 또는 ERROR 레벨로 로그를 남기고 무시해요. | ExceptionHandler | |
| exchangePattern (consumer (advanced)) | |||
| consumer가 exchange를 만들 때의 교환 패턴을 설정해요. |
Enum values:
-
InOnly
-
InOut
| | ExchangePattern | | failOnDuplicateFileAttachment (consumer (advanced)) | 메일 메시지에 파일 이름이 중복된 첨부 파일이 있을 때 메일 처리를 실패로 만들지 여부예요. false로 두면 중복 첨부는 건너뛰고 WARN 로그를 남겨요. true로 두면 예외를 던져서 메일 메시지 처리가 실패해요. | false | boolean | | fetchSize (consumer (advanced)) | 한 번의 폴링에서 소비할 메시지 수의 최댓값을 정해요. 메일박스 폴더에 메시지가 많을 때 메일 서버에 과부하가 걸리는 걸 막는 데 쓸 수 있어요. 기본값 -1은 fetch size가 없다는 뜻이고 모든 메시지를 소비해요. 0으로 설정하면 특수한 코너 케이스로, Camel이 메시지를 전혀 소비하지 않아요. | -1 | int | | folderName (consumer (advanced)) | 폴링할 폴더예요. | INBOX | String | | generateMissingAttachmentNames (consumer (advanced)) | 첨부 파일에 파일 이름이 없을 때 'uuid'로 설정하면 파일 이름에 UUID를 붙여 줘요. | | String | | handleDuplicateAttachmentNames (consumer (advanced)) | 첨부 파일 이름이 중복될 때의 처리 전략을 정해요. never: 첨부 목록에 이미 있는 파일 이름을 가진 첨부는 failOnDuplicateFileAttachment가 true로 설정돼 있지 않은 한 무시돼요. uuidPrefix: 중복 첨부 파일 이름 앞에 각각 uuid와 언더스코어를 붙여요(uuid_filename.fileextension). uuidSuffix: 중복 첨부 파일 이름 뒤에 언더스코어와 uuid를 붙여요(filename_uuid.fileextension). | | String | | mailUidGenerator (consumer (advanced)) | 메일 메시지의 UUID를 만드는 커스텀 로직을 쓸 수 있게 해 주는 플러그형 MailUidGenerator예요. | | MailUidGenerator | | mapMailMessage (consumer (advanced)) | 받은 메일 메시지를 Camel body/headers/attachments로 매핑할지 여부를 정해요. true로 설정하면 메일 메시지 본문이 Camel IN 메시지의 본문으로, 메일 헤더는 IN 헤더로, 첨부는 Camel IN 첨부 메시지로 매핑돼요. 이 옵션을 false로 설정하면 IN 메시지에 raw jakarta.mail.Message가 담겨요. 이 raw 메시지는 exchange.getIn().getBody(jakarta.mail.Message.class)를 호출해서 꺼낼 수 있어요. | true | boolean | | pollStrategy (consumer (advanced)) | 플러그형 org.apache.camel.PollingConsumerPollingStrategy예요. Exchange가 만들어져 Camel에서 라우팅되기 전에 폴링 작업 중에 보통 발생하는 에러 처리를 제어하는 커스텀 구현을 제공할 수 있어요. | | PollingConsumerPollStrategy | | postProcessAction (consumer (advanced)) | 일반 처리가 끝난 뒤 메일박스에 후처리 작업을 하기 위한 MailBoxPostProcessAction을 참조해요. | | MailBoxPostProcessAction | | bcc (producer) | BCC 이메일 주소를 설정해요. 여러 이메일 주소는 쉼표로 구분하세요. | | String | | cc (producer) | CC 이메일 주소를 설정해요. 여러 이메일 주소는 쉼표로 구분하세요. | | String | | from (producer) | 보내는 사람(from) 이메일 주소예요. | camel@localhost | String | | replyTo (producer) | Reply-To 수신자(응답 메일을 받을 사람)예요. 여러 이메일 주소는 쉼표로 구분하세요. | | String | | subject (producer) | 보내는 메시지의 제목(Subject)이에요. 참고: 헤더에서 제목을 설정하면 이 옵션보다 우선해요. | | String | | to (producer) | 받는(목적지) 이메일 주소를 설정해요. 여러 이메일 주소는 쉼표로 구분하세요. | | String | | useHeaderFrom (producer) | 메시지 헤더 From과 Sender가 엔드포인트 URI에 미리 설정된 보내는 사람을 덮어쓸지 여부예요. 기본값은 false예요. true로 두면 메시지 헤더 From과 Sender가 엔드포인트 URI의 보내는 사람을 덮어써요. | false | boolean | | useHeaderRecipients (producer) | 메시지 헤더 To, CC, BCC가 엔드포인트 URI에 미리 설정된 수신자를 덮어쓸지 여부예요. 기본값은 false예요. true로 두면 메시지 헤더가 엔드포인트 URI의 수신자를 덮어써요. | false | boolean | | useHeaderReplyTo (producer) | 메시지 헤더 Reply-To가 엔드포인트 URI에 미리 설정된 replyTo를 덮어쓸지 여부예요. 기본값은 false예요. true로 두면 메시지 Reply-To 헤더가 엔드포인트 URI의 replyTo를 덮어써요. | false | boolean | | useHeaderSubject (producer) | 메시지 헤더 Subject가 엔드포인트 URI에 미리 설정된 subject를 덮어쓸지 여부예요. 기본값은 false예요. true로 두면 메시지 Subject 헤더가 엔드포인트 URI의 subject를 덮어써요. | false | boolean | | javaMailSender (producer (advanced)) | 이메일을 보낼 때 커스텀 org.apache.camel.component.mail.JavaMailSender를 사용하게 해 줘요. | | JavaMailSender | | lazyStartProducer (producer (advanced)) | 프로듀서를 느리게(lazy, 첫 메시지가 올 때) 시작할지 여부예요. 느리게 시작하면, 프로듀서가 시작 중에 실패해서 라우트 시작 자체가 실패하는 상황에서도 CamelContext와 라우트를 시작할 수 있어요. 시작을 늦춰 두면 실패가 발생했을 때 메시지를 라우팅하는 동안 Camel의 라우팅 에러 핸들러로 처리할 수 있죠. 단, 첫 메시지를 처리할 때 프로듀서를 만들고 시작하는 데 시간이 조금 걸려서 전체 처리 시간이 길어질 수 있다는 점은 감안하세요. | false | boolean | | additionalJavaMailProperties (advanced) | 추가 JavaMail 프로퍼티를 설정해요. 다른 모든 옵션으로부터 설정된 기본 프로퍼티에 추가하거나 그걸 덮어써요. 특수한 옵션을 몇 개 추가하면서 나머지는 그대로 두고 싶을 때 유용해요. 접두사가 mail인 다중 값 옵션이에요. | | Properties | | alternativeBodyHeader (advanced) | 대체 이메일 본문을 담고 있는 IN 메시지 헤더의 키를 지정해요. 예를 들어 text/html 형식으로 이메일을 보내면서 HTML을 지원하지 않는 이메일 클라이언트를 위한 대체 본문을 제공하고 싶다면, 이 키를 헤더로 넣어 대체 메일 본문을 설정하세요. | CamelMailAlternativeBody | String | | attachmentsContentTransferEncodingResolver (advanced) | 첨부 파일에 어떤 content-type-encoding을 쓸지 결정하는 커스텀 AttachmentsContentTransferEncodingResolver를 사용하게 해 줘요. | | AttachmentsContentTransferEncodingResolver | | authenticator (advanced) | 로그인에 쓰이는 authenticator예요. 설정하면 password와 username은 무시돼요. 만료될 수 있어서 동적으로 읽어야 하는 토큰에 사용할 수 있어요. | | MailAuthenticator | | binding (advanced) | Camel 메시지와 Mail 메시지를 서로 변환할 때 쓰는 binding을 설정해요. | | MailBinding | | connectionTimeout (advanced) | 연결 타임아웃(밀리초)이에요. | 30000 | int | | contentType (advanced) | 메일 메시지의 content type이에요. HTML 메일에는 text/html을 사용하세요. | text/plain | String | | contentTypeResolver (advanced) | 파일 첨부의 Content-Type을 결정하는 resolver예요. | | ContentTypeResolver | | debugMode (advanced) | 하위 메일 프레임워크에서 디버그 모드를 켜요. SUN Mail 프레임워크는 기본적으로 디버그 메시지를 System.out에 기록해요. | false | boolean | | headerFilterStrategy (advanced) | 헤더를 필터링할 커스텀 org.apache.camel.spi.HeaderFilterStrategy를 사용하게 해 줘요. | | HeaderFilterStrategy | | ignoreUnsupportedCharset (advanced) | 메일을 보낼 때 로컬 JVM이 지원하지 않는 charset을 Camel이 무시하게 하는 옵션이에요. charset이 지원되지 않으면 content-type에서 charset=XXX(XXX는 지원되지 않는 charset)를 제거하고, 대신 플랫폼 기본값에 의존해요. | false | boolean | | ignoreUriScheme (advanced) | 메일을 보낼 때 로컬 JVM이 지원하지 않는 charset을 Camel이 무시하게 하는 옵션이에요. charset이 지원되지 않으면 content-type에서 charset=XXX(XXX는 지원되지 않는 charset)를 제거하고, 대신 플랫폼 기본값에 의존해요. | false | boolean | | javaMailProperties (advanced) | JavaMail 옵션을 설정해요. 기본 프로퍼티를 모두 지우고 이 메서드로 제공된 프로퍼티만 사용해요. | | Properties | | maxMultipartDepth (advanced) | 첨부를 추출할 때 컨슈머가 내려가는 multipart MIME 파트의 최대 중첩 깊이예요. 이보다 깊게 중첩된 메시지는 더 깊은 파트를 건너뛰어서, 악의적으로 만든 아주 깊은 multipart가 스택을 소진하지 못하게 해요. 정상적인 소스가 비정상적으로 깊은 중첩을 만들 때만 늘리세요. | 20 | int | | session (advanced) | 모든 메일 상호작용에 Camel이 사용할 메일 세션을 지정해요. JavaEE 컨테이너처럼 다른 리소스가 메일 세션을 만들고 관리하는 시나리오에서 유용해요. 커스텀 메일 세션을 쓰면 세션에 설정돼 있는 경우 세션의 호스트명과 포트를 사용해요. | | Session | | useInlineAttachments (advanced) | disposition을 inline으로 쓸지 attachment로 쓸지 여부예요. | false | boolean | | idempotentRepository (filter) | 같은 메일박스에서 클러스터로 소비할 수 있게 해 주고, 메일 메시지가 컨슈머가 처리하기에 유효한지 리포지터리가 조율하게 해 주는 플러그형 리포지터리(org.apache.camel.spi.IdempotentRepository)예요. 기본으로는 리포지터리를 쓰지 않아요. | | IdempotentRepository | | idempotentRepositoryRemoveOnCommit (filter) | idempotent 리포지터리를 쓸 때, 메일 메시지가 성공적으로 처리되고 커밋됐다면 메시지 id를 리포지터리에서 지울지(기본값) 리포지터리에 계속 둘지 정해요. 기본으로는 메시지 id가 고유하고 리포지터리에 둘 가치가 없다고 가정해요. 메일 메시지는 다시 소비되는 걸 막으려고 seen/moved/deleted로 표시되기 때문이에요. 따라서 메시지 id를 idempotent 리포지터리에 저장하는 건 별 가치가 없어요. 그래도 이 옵션을 쓰면 어떤 이유에서든 메시지 id를 저장할 수 있어요. | true | boolean | | searchTerm (filter) | 제목, 본문, 보낸 사람, 특정 날짜 이후 발송 같은 검색 조건으로 메일을 걸러낼 수 있게 해 주는 jakarta.mail.search.SearchTerm을 참조해요. 접두사가 searchTerm인 다중 값 옵션이에요. | | SearchTerm | | backoffErrorThreshold (scheduler) | backoffMultipler가 동작하기 전에 연속으로 발생해야 하는 에러 폴링(오류로 실패한 폴링) 횟수예요. | | int | | backoffIdleThreshold (scheduler) | backoffMultipler가 동작하기 전에 연속으로 발생해야 하는 유휴(idle) 폴링 횟수예요. | | int | | backoffMultiplier (scheduler) | 연속으로 유휴/에러 폴링이 이어지면 예약 폴링 컨슈머가 백오프하도록 해요. multiplier 값은 다음 실제 시도 전에 건너뛸 폴링 횟수예요. 이 옵션을 쓸 때는 backoffIdleThreshold와/또는 backoffErrorThreshold도 함께 설정해야 해요. | | int | | delay (scheduler) | 다음 폴링 전에 기다리는 밀리초예요. | 60000 | long | | greedy (scheduler) | greedy를 켜면 이전 폴링에서 메시지를 1개 이상 가져왔을 때 ScheduledPollConsumer가 바로 다시 실행돼요. | false | boolean | | initialDelay (scheduler) | 첫 폴링이 시작되기 전에 기다리는 밀리초예요. | 1000 | long | | repeatCount (scheduler) | 발동(fire) 횟수의 최대 한도를 지정해요. 1로 설정하면 스케줄러가 한 번만 발동하고, 5로 설정하면 다섯 번만 발동해요. 0이나 음수는 계속 발동한다는 뜻이에요. | 0 | long | | runLoggingLevel (scheduler) |
컨슈머가 폴링할 때 시작/완료 로그 줄을 남겨요. 이 옵션으로 그 로깅 레벨을 설정할 수 있어요.
Enum values:
-
TRACE
-
DEBUG
-
INFO
-
WARN
-
ERROR
-
OFF
| TRACE | LoggingLevel | | scheduledExecutorService (scheduler) | 컨슈머가 쓸 커스텀/공유 스레드 풀을 설정하게 해 줘요. 기본으로 컨슈머마다 단일 스레드 풀을 하나씩 가져요. | | ScheduledExecutorService | | scheduler (scheduler) | camel-spring이나 camel-quartz 컴포넌트의 cron 스케줄러를 쓸 때 사용해요. 내장 스케줄러로는 spring 또는 quartz 값을 쓰세요. | none | Object | | schedulerProperties (scheduler) | 커스텀 스케줄러나 Quartz·Spring 기반 스케줄러를 쓸 때 추가 프로퍼티를 설정해요. 접두사가 scheduler인 다중 값 옵션이에요. | | Map | | startScheduler (scheduler) | 스케줄러를 자동으로 시작할지 여부예요. | true | boolean | | timeUnit (scheduler) |
initialDelay와 delay 옵션의 시간 단위예요.
Enum values:
-
NANOSECONDS
-
MICROSECONDS
-
MILLISECONDS
-
SECONDS
-
MINUTES
-
HOURS
-
DAYS
| MILLISECONDS | TimeUnit | | useFixedDelay (scheduler) | 고정 지연(fixed delay)을 쓸지 고정 비율(fixed rate)을 쓸지 제어해요. 자세한 내용은 JDK의 ScheduledExecutorService를 보세요. | true | boolean | | password (security) | 로그인 비밀번호예요. setAuthenticator(MailAuthenticator)도 참고하세요. | | String | | sslContextParameters (security) | SSLContextParameters로 보안을 설정할 때 사용해요. | | SSLContextParameters | | useJavaMailSessionPropertiesFromHeaders (security) | 동적인 JavaMail 세션 프로퍼티(mail.smtp. 또는 mail.smtps.로 시작하는 키의 메시지 헤더)가 메시지별로 엔드포인트 설정을 덮어쓰도록 허용할지 여부예요. 기본으로는 비활성화돼 있어요. 이 헤더가 오직 신뢰할 수 있는 라우트 로직에서 나올 때만 켜고, 신뢰 경계를 넘는 데이터(예: HTTP 쿼리 파라미터, 신뢰할 수 없는 생산자의 JMS/Kafka 메시지)에서 나올 때는 절대 켜지 마세요. 켜 놓으면 이 헤더를 설정할 수 있는 공격자가 전송 보안(mail.smtp.ssl.trust나 mail.smtp.starttls.enable 같은 것)을 약화시키거나 SMTP 연결을 다른 곳으로 돌릴 수 있어요. | false | boolean | | username (security) | 로그인 사용자 이름이에요. setAuthenticator(MailAuthenticator)도 참고하세요. | | String | | sortTerm (sort) | 메시지의 정렬 순서예요. IMAP에서만 기본으로 지원해요. POP3를 쓰거나 IMAP 서버에 SORT 기능이 없을 때는 어느 정도 흉내만 내요. | | SortTerm[] |
메시지 헤더 (Message Headers)
Mail 컴포넌트가 지원하는 메시지 헤더는 아래와 같아요:
| Name | Description | Default | Type |
|---|---|---|---|
Subject (common) Constant: MAIL_SUBJECT |
제목(Subject)이에요. | String | |
From (common) Constant: MAIL_FROM |
보내는 사람(From)이에요. | String | |
To (common) Constant: MAIL_TO |
받는 사람(To)이에요. | String | |
Cc (common) Constant: MAIL_CC |
참조(Cc)예요. | String | |
Bcc (common) Constant: MAIL_BCC |
숨은 참조(Bcc)예요. | String | |
Reply-To (common) Constant: MAIL_REPLY_TO |
회신 주소(Reply-To)예요. | String | |
contentType (common) Constant: MAIL_CONTENT_TYPE |
content type이에요. | String | |
CamelMailCopyTo (consumer) Constant: MAIL_COPY_TO |
메일 메시지를 처리한 후, 지정한 이름의 메일 폴더로 복사할 수 있어요. | String | |
CamelMailMoveTo (consumer) Constant: MAIL_MOVE_TO |
메일 메시지를 처리한 후, 지정한 이름의 메일 폴더로 이동할 수 있어요. | String | |
CamelMailDelete (consumer) Constant: MAIL_DELETE |
메시지를 처리한 후 삭제해요. | boolean | |
CamelMailMessageId (common) Constant: MAIL_MESSAGE_ID |
메시지 ID예요. | String |
사용법 (Usage)
보통은 다음처럼 로그인 자격 증명을 URI에 넣어 지정해요:
SMTP 엔드포인트 예시
smtp://[username@]host[:port][?password=somepwd]
또는 username과 password를 둘 다 쿼리 옵션으로 지정할 수도 있어요:
smtp://host[:port]?password=somepwd&username=someuser
예를 들면 이렇게요:
smtp://mycompany.mailserver:30?password=tiger&username=scott
컴포넌트 별칭 이름 (Component alias names)
-
IMAP -
IMAPs -
POP3s -
POP3s -
SMTP -
SMTPs
기본 포트 (Default ports)
기본 포트 번호를 지원해요. 포트 번호를 생략하면 Camel이 프로토콜에 따라 쓸 포트 번호를 알아서 정해요.
| Protocol | Default Port Number |
|---|---|
SMTP |
25 |
SMTPS |
465 |
POP3 |
110 |
POP3S |
995 |
IMAP |
143 |
IMAPS |
993 |
SSL 지원 (SSL support)
SSL 지원을 제공하는 건 하위 메일 프레임워크의 몫이에요. SSL/TLS 지원은 필요한 Java Mail API 설정 옵션을 전부 직접 지정해서 설정할 수도 있고, 컴포넌트나 엔드포인트 설정을 통해 구성된 SSLContextParameters를 넘겨 줄 수도 있어요.
JSSE Configuration Utility 사용 (Using the JSSE Configuration Utility)
mail 컴포넌트는 Camel JSSE Configuration Utility를 통한 SSL/TLS 설정을 지원해요. 이 유틸리티는 컴포넌트별 코드를 크게 줄여 주고, 엔드포인트와 컴포넌트 레벨에서 설정할 수 있어요. 다음 예시들은 mail 컴포넌트에서 이 유틸리티를 쓰는 방법을 보여 줘요.
엔드포인트의 프로그래밍 방식 설정
Java-only: 프로그래밍 방식 SSLContextParameters 설정과 레지스트리 바인딩
KeyStoreParameters ksp = new KeyStoreParameters();
ksp.setResource("/users/home/server/truststore.jks");
ksp.setPassword("keystorePassword");
TrustManagersParameters tmp = new TrustManagersParameters();
tmp.setKeyStore(ksp);
SSLContextParameters scp = new SSLContextParameters();
scp.setTrustManagers(tmp);
Registry registry = ...
registry.bind("sslContextParameters", scp);
...
from(...)
.to("smtps://smtp.google.com?username=user@gmail.com&password=password&sslContextParameters=#sslContextParameters");
Spring DSL 기반 엔드포인트 설정
...
<camel:sslContextParameters id="sslContextParameters">
<camel:trustManagers>
<camel:keyStore resource="/users/home/server/truststore.jks" password="keystorePassword"/>
</camel:trustManagers>
</camel:sslContextParameters>...
...
<to uri="smtps://smtp.google.com?username=user@gmail.com&password=password&sslContextParameters=#sslContextParameters"/>...
JavaMail 직접 설정 (Configuring JavaMail Directly)
Camel은 잘 알려진 인증 기관(CA)이 발급한 인증서만 신뢰하는 Jakarta JavaMail을 써요(기본 JVM 신뢰 설정이에요). 직접 발급한 인증서를 쓴다면 CA 인증서를 JVM의 Java trust/key store 파일로 가져오거나 기본 JVM trust/key store 파일을 대체해야 해요(자세한 내용은 JavaMail의 SSLNOTES.txt를 보세요).
메일 메시지 내용 (Mail Message Content)
Camel은 메시지 교환의 IN 본문을 MimeMessage의 텍스트 내용으로 써요. 본문은 String.class로 변환돼요.
Camel은 exchange의 IN 헤더를 모두 MimeMessage의 헤더로 복사해요.
MimeMessage의 제목은 IN 메시지의 헤더 프로퍼티로 설정할 수 있어요. 아래 코드가 이를 보여 줘요:
-
Java
-
XML
-
YAML
from("direct:a").setHeader("subject", constant(subject)).to("smtp://james2@localhost");
<route>
<from uri="direct:a"/>
<setHeader name="subject">
<constant>some subject</constant>
</setHeader>
<to uri="smtp://james2@localhost"/>
</route>
- route:
from:
uri: direct:a
steps:
- setHeader:
name: subject
constant: "some subject"
- to:
uri: smtp://james2@localhost
수신자 같은 다른 MimeMessage 헤더에도 똑같이 적용돼요. 헤더 프로퍼티를 To로 쓸 수 있어요:
Java-only: 메일 헤더를 담은 ProducerTemplate API
Map<String, Object> headers = new HashMap<String, Object>();
headers.put("To", "davsclaus@apache.org");
headers.put("From", "jstrachan@apache.org");
headers.put("Subject", "Camel rocks");
headers.put("CamelFileName", "fileOne");
headers.put("org.apache.camel.test", "value");
String body = "Hello Claus.\nYes it does.\n\nRegards James.";
template.sendBodyAndHeaders("smtp://davsclaus@apache.org", body, headers);
MailProducer로 메일을 서버에 보낼 때는 Camel 메시지 헤더에서 CamelMailMessageId 키로 MimeMessage의 메시지 id를 꺼낼 수 있어야 해요.
헤더가 미리 설정된 수신자보다 우선 (Headers take precedence over pre-configured recipients)
메시지 헤더에 지정한 수신자는 항상 엔드포인트 URI에 미리 설정된 수신자보다 우선해요. 메시지 헤더에 수신자를 넣었다면 그게 쓰인다는 발상이에요. 엔드포인트 URI에 미리 설정된 수신자는 폴백(fallback)으로 취급돼요.
아래 예제 코드에서 이메일 메시지는 davsclaus@apache.org로 보내져요. 미리 설정된 수신자인 info@mycompany.com보다 우선하기 때문이에요. 엔드포인트 URI의 CC와 BCC 설정도 무시되고, 그 수신자들은 어떤 메일도 받지 못해요. 헤더와 미리 설정된 설정 사이의 선택은 전부 아니면 전무예요: mail 컴포넌트는 수신자를 헤더에서만 가져오거나 미리 설정된 설정에서만 가져와요. 헤더와 미리 설정된 설정을 섞어서 쓸 수는 없어요.
Java-only: 미리 설정된 수신자를 헤더가 덮어쓰는 모습을 보여 주는 ProducerTemplate API
Map<String, Object> headers = new HashMap<String, Object>();
headers.put("to", "davsclaus@apache.org");
template.sendBodyAndHeaders("smtp://admin@localhost?to=info@mycompany.com", "Hello World", headers);
헤더 덮어쓰기 거부하기 (trust-boundary 강화) (Opting out of header overrides (trust-boundary hardening))
메시지 헤더가 신뢰할 수 없는 소스에서 나오는 라우트(예: HTTP 엔드포인트, JMS 큐, Kafka 토픽에서 브리지된 메시지)에서는, 들어오는 헤더가 수신자·보낸 사람·제목·reply-to 주소를 덮어쓰도록 허용하면 공격자가 메일을 다른 곳으로 돌리거나 응답을 가로채거나 보낸 사람 주소를 위조하거나 임의의 제목을 주입할 수 있어요. 네 개의 옵트인 @UriParam 옵션으로 엔드포인트 설정을 잠가서 메시지 헤더가 덮어쓰지 못하게 할 수 있어요:
| Option | Default | Effect when set to true |
|---|---|---|
useHeaderRecipients |
false |
메시지 To/CC/BCC 헤더가 엔드포인트 URI 수신자를 덮어쓸 수 있어요. |
useHeaderFrom |
false |
메시지 From/Sender 헤더가 엔드포인트 URI 보내는 사람을 덮어쓸 수 있어요. |
useHeaderSubject |
false |
메시지 Subject 헤더가 엔드포인트 URI 제목을 덮어쓸 수 있어요. |
useHeaderReplyTo |
false |
메시지 Reply-To 헤더가 엔드포인트 URI replyTo를 덮어쓸 수 있어요. |
네 옵션 모두 기본값은 false예요. true로 두면 메시지 헤더가 엔드포인트 설정을 덮어써요.
예시:
-
Java
-
XML
-
YAML
.to("smtp://relay@mailhost?to=safe@mycompany.com&from=noreply@mycompany.com&useHeaderRecipients=false&useHeaderFrom=false&useHeaderReplyTo=false");
<to uri="smtp://relay@mailhost?to=safe@mycompany.com&from=noreply@mycompany.com&useHeaderRecipients=false&useHeaderFrom=false&useHeaderReplyTo=false"/>
- route:
from:
uri: direct:send
steps:
- to:
uri: smtp://relay@mailhost
parameters:
to: "safe@mycompany.com"
from: "noreply@mycompany.com"
useHeaderRecipients: false
useHeaderFrom: false
useHeaderReplyTo: false
참고 이 옵션들은 exchange에서 헤더를 제거하지 않아요. Security Model 문서의 Deployment hardening 섹션을 보세요.
더 쉬운 설정을 위한 다중 수신자 (Multiple recipients for easier configuration)
쉼표로 구분하거나 세미콜론으로 구분한 목록으로 수신자를 여러 명 설정할 수 있어요. 이건 헤더 설정과 엔드포인트 URI 설정 어느 쪽에도 적용돼요. 예를 들면 이렇게요:
Java-only: 세미콜론으로 구분한 수신자를 담은 프로그래밍 방식 헤더 맵
Map<String, Object> headers = new HashMap<String, Object>();
headers.put("to", "davsclaus@apache.org ; jstrachan@apache.org ; ningjiang@apache.org");
앞의 예제는 구분자 문자로 세미콜론(;)을 써요.
보내는 사람 이름과 이메일 설정 (Setting sender name and email)
수신자를 name <email> 형식으로 지정하면 이름과 이메일 주소를 둘 다 담을 수 있어요.
예를 들어 메시지에 다음 헤더들을 정의해요:
Java-only: 프로그래밍 방식 헤더 맵 구성
Map headers = new HashMap();
map.put("Subject", "Camel is cool");
map.put("From", "James Strachan <jstrachan@apache.org>");
map.put("To", "Claus Ibsen <davsclaus@apache.org>");
map.put("Cc", "An Other <another@example.com>");
map.put("Bcc", "An Other <another@example.com>");
map.put("Reply-To", "An Other <another@example.com>");
JavaMail API (ex SUN JavaMail)
메일을 소비하고 생산하는 밑바닥에는 JavaMail API가 쓰여요. POP3나 IMAP 프로토콜을 쓸 때는 이 참고 자료들을 살펴보길 권해요. 특히 POP3는 IMAP보다 기능이 훨씬 제한적이라는 점에 유의하세요.
-
And generally about the MAIL Flags
폴링 최적화 (Polling Optimization)
maxMessagePerPoll과 fetchSize 파라미터로 각 폴링에서 처리할 메시지 수를 제한할 수 있어요. 메시지가 많은 폴더를 다룰 때 성능 저하를 막는 데 도움이 되죠. 이전 버전에서는 이 파라미터들이 너무 늦게 평가돼서 큰 메일박스가 여전히 성능 문제를 일으킬 수 있었어요. Camel 3.1부터는 이 파라미터들이 폴링 중에 더 일찍 평가되어 이런 문제를 피할 수 있게 됐어요.
예시 (Examples)
JMS 큐에서 받은 메시지를 이메일로 보내는 간단한 라우트로 시작해 볼게요. 이메일 계정은 mymailserver.com의 admin 계정이에요.
-
Java
-
XML
-
YAML
from("jms://queue:subscription").to("smtp://admin@mymailserver.com?password=secret");
<route>
<from uri="jms://queue:subscription"/>
<to uri="smtp://admin@mymailserver.com?password=secret"/>
</route>
- route:
from:
uri: jms://queue:subscription
steps:
- to:
uri: smtp://admin@mymailserver.com
parameters:
password: secret
다음 예제에서는 1분에 한 번씩 메일박스에서 새 이메일을 폴링해요.
-
Java
-
XML
-
YAML
from("imap://admin@mymailserver.com?password=secret&unseen=true&delay=60000")
.to("seda://mails");
<route>
<from uri="imap://admin@mymailserver.com?password=secret&unseen=true&delay=60000"/>
<to uri="seda://mails"/>
</route>
- route:
from:
uri: imap://admin@mymailserver.com
parameters:
password: secret
unseen: true
delay: 60000
steps:
- to:
uri: seda://mails
첨부 파일을 담아 메일 보내기 (Sending mail with attachment)
경고 모든 Camel 컴포넌트가 첨부를 지원하는 것은 아님 (Attachments are not supported by all Camel components)
_Attachments API_는 Java Activation Framework에 기반하고, 일반적으로 Mail API만 이걸 써요. 다른 많은 Camel 컴포넌트는 첨부를 지원하지 않기 때문에, 라우트를 따라 전파되는 동안 첨부가 유실될 수 있어요. 따라서 경험 법칙은, 메일 엔드포인트로 메시지를 보내기 직전에 첨부를 추가하는 거예요.
mail 컴포넌트는 첨부 파일을 지원해요. 아래 예제에서는 일반 텍스트 메시지에 로고 파일 첨부를 담은 메일 메시지를 보내요.
Java-only: Camel API로 Exchange와 첨부를 다루는 프로그래밍 방식
// create an exchange with a normal body and attachment to be produced as email
Endpoint endpoint = context.getEndpoint("smtp://james@mymailserver.com?password=secret");
// create the exchange with the mail message that is multipart with a file and a Hello World text/plain message.
Exchange exchange = endpoint.createExchange();
AttachmentMessage in = exchange.getIn(AttachmentMessage.class);
in.setBody("Hello World");
DefaultAttachment att = new DefaultAttachment(new FileDataSource("src/test/data/logo.jpeg"));
att.addHeader("Content-Description", "some sample content");
in.addAttachmentObject("logo.jpeg", att);
// create a producer that can produce the exchange (= send the mail)
Producer producer = endpoint.createProducer();
// start the producer
producer.start();
// and let it go (processes the exchange by sending the email)
producer.process(exchange);
SSL 예제 (SSL example)
이 예제에서는 Google Mail 받은 편지함에서 메일을 폴링하려고 해요. 로컬 메일 클라이언트로 메일을 내려받으려면 Google Mail이 SSL을 켜고 설정하도록 요구해요. Google Mail 계정에 로그인해서 IMAP 접근을 허용하도록 설정을 바꾸면 돼요. 방법에 대해서는 Google의 상세한 문서가 있어요.
-
Java
-
XML
-
YAML
from("imaps://imap.gmail.com?username=YOUR_USERNAME@gmail.com&password=YOUR_PASSWORD&delete=false&unseen=true&delay=60000")
.to("log:newmail");
<route>
<from uri="imaps://imap.gmail.com?username=YOUR_USERNAME@gmail.com&password=YOUR_PASSWORD&delete=false&unseen=true&delay=60000"/>
<to uri="log:newmail"/>
</route>
- route:
from:
uri: imaps://imap.gmail.com
parameters:
username: "YOUR_USERNAME@gmail.com"
password: YOUR_PASSWORD
delete: false
unseen: true
delay: 60000
steps:
- to:
uri: log:newmail
앞의 라우트는 1분에 한 번씩 Google Mail 받은 편지함에서 새 메일을 폴링하고, 받은 메시지를 newmail 로거 카테고리에 기록해요. 예제를 DEBUG 로깅을 켠 채로 실행하면 로그에서 진행 상황을 볼 수 있어요:
2008-05-08 06:32:09,640 DEBUG MailConsumer - Connecting to MailStore imaps//imap.gmail.com:993 (SSL enabled), folder=INBOX
2008-05-08 06:32:11,203 DEBUG MailConsumer - Polling mailfolder: imaps//imap.gmail.com:993 (SSL enabled), folder=INBOX
2008-05-08 06:32:11,640 DEBUG MailConsumer - Fetching 1 messages. Total 1 messages.
2008-05-08 06:32:12,171 DEBUG MailConsumer - Processing message: messageNumber=[332], from=[James Bond <007@mi5.co.uk>], to=YOUR_USERNAME@gmail.com], subject=[...
2008-05-08 06:32:12,187 INFO newmail - Exchange[MailMessage: messageNumber=[332], from=[James Bond <007@mi5.co.uk>], to=YOUR_USERNAME@gmail.com], subject=[...
첨부 파일이 있는 메일 소비하기 (Consuming mails with attachment)
이 예제에서는 메일박스를 폴링해서 메일의 모든 첨부를 파일로 저장해요. 먼저 메일박스를 폴링하는 라우트를 정의해요. 이 예제는 Google Mail 기반이라 SSL 예제에서 보여 준 것과 같은 라우트를 써요:
Java-only: 커스텀 Processor로 메일 첨부를 다루는 방식
from("imaps://imap.gmail.com?username=YOUR_USERNAME@gmail.com&password=YOUR_PASSWORD&delete=false&unseen=true&delay=60000")
.process(new MyMailProcessor());
메일을 로그에 기록하는 대신, Java 코드에서 메일을 처리할 수 있는 프로세서를 써요:
Java-only: 첨부를 꺼내 저장하는 커스텀 Processor
public void process(Exchange exchange) throws Exception {
// the API is a bit clunky, so we need to loop
AttachmentMessage attachmentMessage = exchange.getMessage(AttachmentMessage.class);
Map<String, DataHandler> attachments = attachmentMessage.getAttachments();
if (attachments.size() > 0) {
for (String name : attachments.keySet()) {
DataHandler dh = attachments.get(name);
// get the file name
String filename = dh.getName();
// get the content and convert it to byte[]
byte[] data = exchange.getContext().getTypeConverter()
.convertTo(byte[].class, dh.getInputStream());
// write the data to a file
FileOutputStream out = new FileOutputStream(filename);
out.write(data);
out.flush();
out.close();
}
}
}
보다시피 첨부를 다루는 API는 조금 투박하지만 있긴 있어요. javax.activation.DataHandler를 꺼내서 표준 API로 첨부를 다룰 수 있어요.
첨부가 있는 메일 메시지 분할하기 (How to split a mail message with attachments)
이 예제에서는 첨부를 여러 개 가질 수 있는 메일 메시지를 소비해요. 하려는 건 Splitter EIP를 개별 첨부별로 써서 첨부를 따로따로 처리하는 거예요. 예를 들어 메일 메시지에 첨부가 다섯 개 있으면 Splitter가 첨부를 하나씩 담은 메시지 다섯 개를 처리하게 하는 거죠. 이러려면 Splitter에 커스텀 Expression을 제공해서, 첨부를 하나씩 담은 메시지 다섯 개가 들어 있는 List를 넘겨 줘야 해요.
이 코드는 Camel 2.10부터 camel-mail 컴포넌트에 기본으로 들어 있어요. 클래스는 org.apache.camel.component.mail.SplitAttachmentsExpression이고, 소스 코드는 여기에서 볼 수 있어요.
그다음 Camel 라우트에서 이 Expression을 아래와 같이 라우트에 쓰면 돼요:
XML DSL을 쓴다면 Splitter에 메서드 호출 표현식을 아래와 같이 선언해야 해요.
XML-only: beanType 속성을 가진 method 표현식은 XML DSL 전용이에요
<split>
<method beanType="org.apache.camel.component.mail.SplitAttachmentsExpression"/>
<to uri="mock:split"/>
</split>
첨부를 byte[]로 쪼개서 메시지 본문에 담기게 할 수도 있어요. 표현식을 boolean true로 만들면 돼요.
Java-only: 프로그래밍 방식 SplitAttachmentsExpression 설정
SplitAttachmentsExpression split = SplitAttachmentsExpression(true);
그다음 이 표현식을 splitter EIP와 함께 쓰면 돼요.
커스텀 SearchTerm 사용 (Using custom SearchTerm)
MailEndpoint에 searchTerm을 설정해서 원치 않는 메일을 걸러낼 수 있어요.
예를 들어 제목이나 본문에 Camel이 들어 있는 메일만 걸러내려면 다음처럼 하면 돼요:
-
Java
-
XML
-
YAML
from("imaps://mymailseerver?username=foo&password=secret&searchTerm.subjectOrBody=Camel")
.to("bean:myBean");
<route>
<from uri="imaps://mymailseerver?username=foo&password=secret&searchTerm.subjectOrBody=Camel"/>
<to uri="bean:myBean"/>
</route>
- route:
from:
uri: imaps://mymailseerver
parameters:
username: foo
password: secret
searchTerm.subjectOrBody: Camel
steps:
- to:
uri: bean:myBean
"searchTerm.subjectOrBody"를 파라미터 키로 써서 메일 제목이나 본문에 "Camel"이라는 단어가 들어 있기를 검색하려는 걸 나타내는 것에 주목하세요.
org.apache.camel.component.mail.SimpleSearchTerm 클래스에는 설정할 수 있는 옵션이 여러 개 있어요:
또는 24시간 전부터 지금까지의 새 안 읽은 이메일을 가져오려면 다음처럼 해요. "now-24h" 구문에 주목하세요. 자세한 내용은 아래 표를 보세요.
-
Java
-
XML
-
YAML
from("imaps://mymailseerver?username=foo&password=secret&searchTerm.fromSentDate=now-24h")
.to("bean:myBean");
<route>
<from uri="imaps://mymailseerver?username=foo&password=secret&searchTerm.fromSentDate=now-24h"/>
<to uri="bean:myBean"/>
</route>
- route:
from:
uri: imaps://mymailseerver
parameters:
username: foo
password: secret
searchTerm.fromSentDate: now-24h
steps:
- to:
uri: bean:myBean
엔드포인트 URI 설정에 searchTerm을 여러 개 둘 수도 있어요. 그러면 AND 연산자로 결합돼서, 두 조건이 모두 맞아야 해요. 예를 들어 메일 제목에 Camel이 들어 있고 24시간 거슬러 올라가는 안 읽은 이메일을 가져오려면 다음처럼 해요:
-
Java
-
XML
-
YAML
from("imaps://mymailseerver?username=foo&password=secret&searchTerm.subject=Camel&searchTerm.fromSentDate=now-24h")
.to("bean:myBean");
<route>
<from uri="imaps://mymailseerver?username=foo&password=secret&searchTerm.subject=Camel&searchTerm.fromSentDate=now-24h"/>
<to uri="bean:myBean"/>
</route>
- route:
from:
uri: imaps://mymailseerver
parameters:
username: foo
password: secret
searchTerm.subject: Camel
searchTerm.fromSentDate: now-24h
steps:
- to:
uri: bean:myBean
SimpleSearchTerm은 POJO에서 쉽게 설정하도록 설계됐기 때문에, XML에서 스타일로 설정할 수도 있어요.
XML-only: `SimpleSearchTerm`의 Spring <bean> 설정
<bean id="mySearchTerm" class="org.apache.camel.component.mail.SimpleSearchTerm">
<property name="subject" value="Order"/>
<property name="to" value="acme-order@acme.com"/>
<property name="fromSentDate" value="now"/>
</bean>
그다음 Camel 라우트에서 아래처럼 #beanId를 써서 이 빈을 참조할 수 있어요:
-
Java
-
XML
-
YAML
from("imaps://mymailseerver?username=foo&password=secret&searchTerm=#mySearchTerm")
.to("bean:myBean");
<route>
<from uri="imaps://mymailseerver?username=foo&password=secret&searchTerm=#mySearchTerm"/>
<to uri="bean:myBean"/>
</route>
- route:
from:
uri: imaps://mymailseerver
parameters:
username: foo
password: secret
searchTerm: "#mySearchTerm"
steps:
- to:
uri: bean:myBean
Java에는 org.apache.camel.component.mail.SearchTermBuilder 클래스로 복합 SearchTerms를 만드는 빌더 클래스가 있어요. 이걸 쓰면 다음 같은 복잡한 조건을 만들 수 있어요:
Java-only: 복잡한 메일 필터를 위한 프로그래밍 방식 SearchTermBuilder
// we just want the unseen mails that are not spam
SearchTermBuilder builder = new SearchTermBuilder();
builder.unseen().body(Op.not, "Spam").subject(Op.not, "Spam")
// which was sent from either foo or bar
.from("foo@somewhere.com").from(Op.or, "bar@somewhere.com");
// ... and we could continue building the terms
SearchTerm term = builder.build();
추가 Java Mail Sender 프로퍼티와 함께 헤더 사용 (Using headers with additional Java Mail Sender properties)
메일을 보낼 때 Exchange에서 mail.smtp.(또는 mail.smtps.)로 시작하는 키의 메시지 헤더로 JavaMailSender에 동적인 JavaMail 프로퍼티를 넘겨 줄 수 있어요.
Java Mail 문서에서 찾을 수 있는 mail.smtp 프로퍼티는 어떤 것이든 설정할 수 있어요.
예를 들어 mail.smtp.from(SMTP MAIL 명령)에 동적인 uuid를 넘겨 주려면 이렇게 해요:
Java-only: 동적 UUID 생성용 .method() 표현식은 Java DSL 전용이에요
.setHeader("from", constant("reply2me@foo.com"))
.setHeader("mail.smtp.from", method(UUID.class, "randomUUID"))
.to("smtp://mymailserver:1234?useJavaMailSessionPropertiesFromHeaders=true");
참고 이 기능은 커스텀
JavaMailSender를 쓰지 않을 때만 지원돼요.
경고 이 기능은 기본으로 비활성화되어 있고
useJavaMailSessionPropertiesFromHeaders=true로 명시적으로 켜야 해요.mail.smtp.*/mail.smtps.*키는 어떤HeaderFilterStrategy로도 걸러지지 않는 Camel 내부 네임스페이스라서, 이 헤더가 신뢰 경계를 넘는 데이터(예: HTTP 쿼리 파라미터, 신뢰할 수 없는 생산자의 JMS/Kafka 메시지)에서 나올 수 있다면 공격자가 전송 보안(mail.smtp.ssl.trust나mail.smtp.starttls.enable같은 것)을 약화시키거나 SMTP 연결을 다른 곳으로 돌릴 수 있어요. 이 헤더가 오직 신뢰할 수 있는 라우트 로직에 의해서만 설정될 때만 옵션을 켜고, 신뢰할 수 없는 소스를 브리지할 때는 producer 앞에서removeHeaders("mail.smtp.*", "mail.smtps.*")로 네임스페이스를 제거하세요. 일반적인 패턴은 Camel Security Model을 보세요.
더 알아보기 (Learn more)
- 메일 본문·헤더·첨부가 어떻게 매핑되는지 궁금하면 메일 메시지 내용 (Mail Message Content) 섹션을 보세요. 첨부 파일을 담아 보내고 받는 방법은 첨부 파일을 담아 메일 보내기 (Sending mail with attachment)와 첨부 파일이 있는 메일 소비하기 (Consuming mails with attachment) 예제에 나와 있어요.
- SSL/TLS 설정은 SSL 지원 (SSL support) 섹션의 JSSE Configuration Utility 예제부터 따라 해 보세요. 신뢰할 수 없는 소스의 헤더가 수신자를 덮어쓰지 못하게 하려면 '헤더 덮어쓰기 거부하기' 섹션의
useHeader*옵션을 확인하세요. - 메일을 걸러 내는 방법이 필요하면 커스텀 SearchTerm 사용 (Using custom SearchTerm) 섹션의 searchTerm 조합을 참고하고, POP3는 IMAP보다 기능이 제한적이라는 점을 기억하세요. 보안 관련 경고가 있으면 Camel Security Model 문서도 함께 보면 좋아요.