Spring Properties

API KEY 를 프로퍼티로 설정한 후 호출하는 방법

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/**
 * application.yml에 값 추가
 */
open-api:
  key: API-key값 입력

/**
 * Properties Class 생성
 */
@Data
@Component
@ConfigurationProperties("open-api") // 설정파일의 이름과 동일하게 입력
public class OpenApiProperties {
    private String key;
}

/**
 * 사용할 클래스에서 @RequiredArgsConstructor로 선언 후 사용
 */ 
@RestController
@RequiredArgsConstructor
public class ApiExtraController {
    private final OpenApiProperties openApiProperties;
    
    @GetMapping("/api/extra/air")
    public String air(@RequestBody AirInput airInput) {
        String url = "https://apis.data.go.kr/B552584/ArpltnInforInqireSvc/getCtprvnRltmMesureDnsty?serviceKey=%s&pageNo=1&sidoName=%s&ver=1.0";
        String apiResult = "";
        String key = openApiProperties.getKey();

        try {
            URI uri = new URI(String.format(url, key, URLEncoder.encode(airInput.getSearchSido(), StandardCharsets.UTF_8)));

            RestTemplate restTemplate = new RestTemplate();
            HttpHeaders headers = new HttpHeaders();
            headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
            apiResult = restTemplate.getForObject(uri, String.class);
        } catch (Exception e) {
            log.debug(e.getMessage());
        }

        return apiResult;
    }
}


이메일 전송 설정


build.gradle

1
2
// Spring Boot Starter Mail(이메일 전송 라이브러리)
implementation 'org.springframework.boot:spring-boot-starter-mail:3.2.3'


application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
spring:
  mail:
	host: smtp.gmail.com //SMTP 서버 호스트
    port: 587 // SMTP 서버 포트
    username: uj.mail.send@gmail.com //발신자 이메일
    password: cobnqvncqckvcutn //발신자 앱 비밀번호
    
    properties:
      mail:
        smtp:
          auth: true // 사용자 인증 시도 여부(기본값 false)
          starttls:
            enable: true //StartTLS 활성화 여부(기본값 false)


MailComponent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@Slf4j
@Component
@RequiredArgsConstructor
public class MailComponent {
    private final JavaMailSender javaMailSender;

    public boolean send(String fromEmail, String fromName,
                        String toEmail, String toName,
                        String title, String contents) {

        boolean result = false;
        MimeMessagePreparator mimeMessagePreparator = mimeMessage -> {
            MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true, "UTF-8");
            InternetAddress from = new InternetAddress();
            from.setAddress(fromEmail);
            from.setPersonal(fromName);

            InternetAddress to = new InternetAddress();
            to.setAddress(toEmail);
            to.setPersonal(toName);

            mimeMessageHelper.setFrom(from);
            mimeMessageHelper.setTo(to);
            mimeMessageHelper.setSubject(title);
            mimeMessageHelper.setText(contents, true);
        };

        try {
            javaMailSender.send(mimeMessagePreparator);
            result = true;
        } catch (Exception e) {
            log.info(e.getMessage());
        }

        return result;
    }
}


이메일 전송

Q1) 회원가입시 가입된 회원에게 가입 메일을 전송하는 API를 작성해 보세요.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
@RestController
@RequiredArgsConstructor
public class ApiMemberController {
    private final MemberService memberService;
    
    @PostMapping("/api/public/member")
    public ResponseEntity<?> addMember(@RequestBody MemberInput memberInput) {
        ServiceResult result = memberService.addMember(memberInput);
        return ResponseResult.result(result);
    }
}

public interface MemberService {
    ServiceResult addMember(MemberInput memberInput);
}

@Service
@RequiredArgsConstructor
public class MemberServiceImpl implements MemberService {
    private final MemberRepository memberRepository;
    private final MailComponent mailComponent;

	@Override
    public ServiceResult addMember(MemberInput memberInput) {
        Optional<Member> optionalMember = memberRepository.findByEmail(memberInput.getEmail());
        if (optionalMember.isPresent()) {
            throw new BizException("이미 가입된 이메일 입니다.");
        }

        String encryptedPassword = PasswordUtils.encryptedPassword(memberInput.getPassword());
        Member member = Member.builder()
                .email(memberInput.getEmail())
                .memberName(memberInput.getMemberName())
                .regDate(LocalDateTime.now())
                .password(encryptedPassword)
                .phone(memberInput.getPhone())
                .status(MemberStatus.Using)
                .build();

        memberRepository.save(member);
        
        // 메일을 전송
        String fromEmail = "admin@gmail.com";
        String fromName = "관리자";
        String toEmail = member.getEmail();
        String toName = member.getMemberName();

        String title = "회원가입을 축하드립니다.";
        String contents = "회원가입을 축하드립니다.";

        mailComponent.send(fromEmail, fromName, toEmail, toName, title, contents);

        return ServiceResult.success();
    }
}

Q2) 비밀번호 초기화를 위해서 이메일로 인증코드를 전송하는 API를 작성해 보세요.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
@RestController
@RequiredArgsConstructor
public class ApiMemberController {
    private final MemberRepository memberRepository;
    
    @PostMapping("/api/public/member/password/reset")
    public ResponseEntity<?> resetPassword(
        @RequestBody @Valid MemberPasswordResetInput input, Errors errors) {
        if (errors.hasErrors()) {
            return ResponseResult.fail("입력값이 정확하지 않습니다.", ResponseError.of(errors.getAllErrors()));
        }

        ServiceResult result = null;
        try {
            result = memberService.resetPassword(input);
        } catch (BizException e) {
            return ResponseResult.fail(e.getMessage());
        }
        
        return ResponseResult.result(result);
    }
}

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class MemberPasswordResetInput {
    @Email(message = "이메일 형식이 아닙니다.")
    @NotBlank(message = "이메일은 필수 입력 사항입니다.")
    private String email;

    @NotBlank(message = "이름은 필수 입력 사항입니다.")
    private String memberName;
}

public interface MemberService {
    ServiceResult resetPassword(MemberPasswordResetInput memberPasswordResetInput);
}

@Service
@RequiredArgsConstructor
public class MemberServiceImpl implements MemberService {
    private final MemberRepository memberRepository;
    private final MailComponent mailComponent;
    private final MailTemplateRepository mailTemplateRepository;
    
    @Override
    public ServiceResult resetPassword(MemberPasswordResetInput input) {
        Optional<Member> optionalMember = memberRepository.findByEmailAndMemberName(
                input.getEmail(), input.getMemberName());
        if (optionalMember.isEmpty()) {
            throw new BizException("회원 정보가 존재하지 않습니다.");
        }
        Member member = optionalMember.get();

        String passwordResetKey = UUID.randomUUID().toString();

        member.setPasswordResetYn(true);
        member.setPasswordResetKey(passwordResetKey);
        memberRepository.save(member);

        String serverUrl = "http://localhost:8080";

        Optional<MailTemplate> optionalMailTemplate = mailTemplateRepository.findByTemplateId("MEMBER_PASSWORD_RESET");
        optionalMailTemplate.ifPresent(e -> {
            String fromEmail = e.getSendEmail();
            String fromMemberName = e.getSendMemberName();
            String title = e.getTitle().replaceAll("\\{MEMBER_NAME\\}", member.getMemberName());
            String contents = e.getContents().replaceAll("\\{MEMBER_NAME\\}", member.getMemberName()
                    .replaceAll("\\{SERVER_URL\\}", serverUrl)
                    .replaceAll("\\{RESET_PASSWORD_KEY\\}", passwordResetKey));


            mailComponent.send(fromEmail, fromMemberName, member.getEmail(), member.getMemberName(), title, contents);
        });

        return ServiceResult.success();
    }
}

Q3) 게시판에 글을 작성했을 때 사용자에게 작성된 글의 정보를 메일로 전송하는 API를 작성해 보세요.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/**
 * 기존 글쓰기 로직에 추가
 */ 

@Service
@RequiredArgsConstructor
public class BoardServiceImpl implements BoardService {
    private final BoardTypeRepository boardTypeRepository;
    private final BoardRepository boardRepository;
    private final MemberRepository memberRepository;
    private final MailComponent mailComponent;
    private final MailTemplateRepository mailTemplateRepository;
    
    @Override
    public ServiceResult add(String email, BoardInput boardInput) {
        Optional<Member> optionalMember = memberRepository.findByEmail(email);
        if (optionalMember.isEmpty()) {
            throw new BizException("회원 정보가 존재하지 않습니다.");
        }
        Member member = optionalMember.get();

        Optional<BoardType> optionalBoardType = boardTypeRepository.findById(boardInput.getBoardType());
        if (optionalBoardType.isEmpty()) {
            return ServiceResult.fail("게시판 정보가 존재하지 않습니다.");
        }
        BoardType boardType = optionalBoardType.get();
        Board board = Board.builder()
                .member(member)
                .title(boardInput.getTitle())
                .contents(boardInput.getContents())
                .boardType(boardType)
                .regDate(LocalDateTime.now())
                .build();

        boardRepository.save(board);

        //메일 전송 로직
        Optional<MailTemplate> optionalMailTemplate = mailTemplateRepository.findByTemplateId("BOARD_ADD");
        optionalMailTemplate.ifPresent(e -> {
            String fromEmail = e.getSendEmail();
            String fromMemberName = e.getSendMemberName();
            String title = e.getTitle().replaceAll("\\{MEMBER_NAME\\}", member.getMemberName());
            String contents = e.getContents().replaceAll("\\{BOARD_TITLE\\}", board.getTitle())
                    .replaceAll("\\{BOARD_CONTENTS\\}", board.getContents());

            mailComponent.send(fromEmail, fromMemberName, member.getEmail(), member.getMemberName(), title, contents);
        });

        return ServiceResult.success();
    }
}

Q4) 문의 게시판이 글에 답변을 달았을 때 메일로 답변 정보를 전송하는 API를 작성해 보세요.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class BoardReplyInput {
    private String replyContents;
}

@RequiredArgsConstructor
@RestController
public class ApiAdminBoardController {
    private final BoardService boardService;

	@PostMapping("/api/admin/board/{id}/reply")
    public ResponseEntity<?> reply(@PathVariable Long id,
                                   @RequestBody BoardReplyInput boardReplyInput) {
        ServiceResult result = boardService.replyBoard(id, boardReplyInput);
        return ResponseResult.result(result);
    }
}

public interface BoardService {
	ServiceResult replyBoard(Long id, BoardReplyInput boardReplyInput);
}

@Service
@RequiredArgsConstructor
public class BoardServiceImpl implements BoardService {
    private final BoardRepository boardRepository;
    private final MailComponent mailComponent;
    private final MailTemplateRepository mailTemplateRepository;
    
    @Override
    public ServiceResult replyBoard(Long id, BoardReplyInput boardReplyInput) {
        Optional<Board> optionalBoard = boardRepository.findById(id);
        if (optionalBoard.isEmpty()) {
            return ServiceResult.fail("게시글이 존재하지 않습니다.");
        }
        Board board = optionalBoard.get();

        board.setReplyContents(boardReplyInput.getReplyContents());
        boardRepository.save(board);

        // 메일 전송
        Optional<MailTemplate> optionalMailTemplate = mailTemplateRepository.findByTemplateId("BOARD_REPLY");
        optionalMailTemplate.ifPresent(e -> {
            String fromEmail = e.getSendEmail();
            String fromMemberName = e.getSendMemberName();
            String title = e.getTitle().replaceAll("\\{MEMBER_NAME\\}", board.getMember().getMemberName());
            String contents = e.getContents().replaceAll("\\{BOARD_TITLE\\}", board.getTitle())
                    .replaceAll("\\{BOARD_CONTENTS\\}", board.getContents())
                    .replaceAll("\\{BOARD_REPLY_CONTENTS\\}", board.getReplyContents());

            mailComponent.send(fromEmail, fromMemberName, 
                    board.getMember().getEmail(), board.getMember().getMemberName(), title, contents);
        });
        
        return ServiceResult.success();
    }
    
}

Q5) 스프링 스케쥴러를 이용하여 매일 새벽4시에 로그정보를 삭제하는 기능을 작성해 보세요.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public interface LogService {
    void deleteLog();
}

@RequiredArgsConstructor
@Service
public class LogServiceImpl implements LogService {
    private final LogsRepository logsRepository;
    
    @Override
    public void deleteLog() {
        logsRepository.deleteAll();
    }
}

@Component
@RequiredArgsConstructor
public class Scheduler {
    private final LogService logService;

    @Scheduled(cron = "0 0 4 * * *")
    public void deleteLog() {
        logService.deleteLog();
    }
}

@EnableAspectJAutoProxy //AOP
@EnableScheduling //Schedule
@SpringBootApplication
public class SampleApplication {
    public static void main(String[] args) {
        SpringApplication.run(SampleApplication.class, args);
    }
}

Q6) 스프링 스케쥴러를 이용하여 회원중 가입일이 1년이 도래한 회원에 대해서

​ 서비스 통지 메일을 보내는 기능을 작성해 보세요.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public interface MemberService {
    void sendServiceNotice();
}

@Service
@RequiredArgsConstructor
public class MemberServiceImpl implements MemberService {
    private final MemberRepository memberRepository;
    private final MailComponent mailComponent;
    private final MailTemplateRepository mailTemplateRepository;
    
    @Override
    public void sendServiceNotice() {
        Optional<MailTemplate> optionalMailTemplate = mailTemplateRepository.findByTemplateId("MEMBER_SERVICE_NOTICE");
        optionalMailTemplate.ifPresent(e -> {

            String fromEmail = e.getSendEmail();
            String fromMemberName = e.getSendMemberName();
            String contents = e.getContents();

            memberRepository.findAll().forEach(u -> {
                String title = e.getTitle().replaceAll("\\{MEMBER_NAME\\}", u.getMemberName());
                mailComponent.send(fromEmail, fromMemberName, u.getEmail(), u.getMemberName(), title, contents);
            });
        });
    }
}

@Component
@RequiredArgsConstructor
public class Scheduler {
    private final MemberService memberService;

    @Scheduled(cron = "0 0 0 1 1 *")
    public void sendServiceNotice() {
        memberService.sendServiceNotice();
    }
}

이메일 전송메세지를 DB에 템플릿 형태로 저장

Entity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Entity
public class MailTemplate {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String templateId;

    private String title;
    private String contents;

    private String sendEmail;
    private String sendMemberName;

    private LocalDateTime regDate;

}


Repository

1
2
3
4
@Repository
public interface MailTemplateRepository extends JpaRepository<MailTemplate, Long> {
    Optional<MailTemplate> findByTemplateId(String templateId);
}


SQL Query

1
2
3
4
5
6
7
INSERT INTO MAIL_TEMPLATE(TEMPLATE_ID, TITLE, CONTENTS, SEND_EMAIL, SEND_MEMBER_NAME, REG_DATE)
VALUES ('MEMBER_PASSWORD_RESET', '{MEMBER_NAME}님의 비밀번호 초기화 요청입니다.',
        '<div><p>{MEMBER_NAME}님 안녕하세요</p><p>아래 링크를 클릭하여, 비밀번호를 초기화 해주세요.</p><p><a href="{SERVER_URL}/reset?key={RESET_PASSWORD_KEY}">초기화</a></p></div>',
        'uj.mail.send@gmail.com', '관리자', '2022-02-02 01:12:20.000000'),
       ('BOARD_ADD', '{MEMBER_NAME}님이 글을 게시하였습니다.',
        '<div><p>제목 : {BOARD_TITLE}</p><p>내용</p><div>{BOARD_CONTENTS}</div></div>',
        'uj.mail.send@gmail.com', '관리자', '2022-02-02 01:12:20.000000');

카테고리:

업데이트:

댓글남기기