[PEANUT] - 이메일을 이용한 환자 보호자 연결 기능

Yu Seong Kim·2024년 10월 20일
0

peanut

목록 보기
1/1
post-thumbnail

개요

PEANUT의 메인 기능 중 하나는 보호자-환자 관리 시스템입니다.
PEANUT 서비스의 보호자-환자 관리란? 환자의 당수치,복약,인슐린 투약 등 다양한 정보를 환자와 보호자가 서로 공유하며 환자의 상태를 모니터링하고, 관리하는 기능입니다.

'환자와 보호자를 연결하기위해 어떠한 방법이 있을까?'라는 의문점을 두고, 많은 방법을 생각했습니다.

연결 방법

  1. CoolSms를 이용한 문자 인증코드로 인증 후 연결
  2. 이메일 인증번호를 이용하여 인증코드를 인증 후 연결
  3. 실시간 알림을 환자에게 보낸 다음 인증 코드로 연결

다양한 방법을 생각했지만, 이메일 인증을 수행하여 환자와 보호자를 연결하는 방법을 선택했습니다.

이메일인증으로 선택한 이유

간단하게 요약하자면,
1. 보편적 사용성: 대부분의 사용자들이 이미 익숙하게 사용하는 수단입니다.
2. 안전성: 암호화와 인증 절차로 개인정보 보호에 유리합니다.
3. 비용 절감: 문자 메시지 인증에 비해 경제적입니다.
4. 확장 가능성: 인증 외에 다양한 정보와 알림을 제공할 수 있는 장점이 있습니다.
5. 높은 성공률: 전송 실패율이 낮고 안정적입니다.

이와 같은 이유로 이메일 인증을 통한 보호자-환자 연결 방식은 PEANUT서비스에 매우 적합하다고 판단하였습니다.

보호자-환자 연결 흐름

<보호자>
1. 보호자가 환자의 이메일을 입력합니다.
2. 이메일에 관한 환자의 정보를 반환하고, 환자가 맞다면 환자에게 연결 요청을 보냅니다.
<환자>
3. 환자는 이메일로 온 인증 코드를 입력한다.

매우 간단한 흐름으로 보호자와 환자를 연결할 수 있습니다.

데이터베이스 관점

보호자가 환자에게 연결 요청을 보내면 연결 요청 대기 테이블에 연결 요청 정보가 저장이 됩니다.
환자가 연결 요청을 받기 전까진 '대기',요청 수락하면 '승인'으로 업데이트 됩니다.

@Entity
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class ConnectionWaiting {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String patientName;

    private String patientEmail;

    private String inviteCode;

    private String guardianEmail;

    private String status;
    
}

보호자와 환자의 엔티티는 따로 관리하지 않습니다. 그 이유는 회원가입을 하는 순간 모두 동일한 환자의 역할이지만 유저가 다른 유저에게 환자 연결 요청을 하여 연결을 맺는 순간 그 유저(연결 요청 송신자)는 보호자 페이지도 접속할 수 있습니다. 즉 동일한 User엔티티에서 관리하지만 따로 연결 테이블을 만들어 연결된 보호자와 환자를 관리하도록 하였습니다.

public class PatientGuardian {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private boolean verified;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "patient_id")
    private User patient;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "guardian_id")
    private User guardian;

    public  PatientGuardian (User patient, User guardian,boolean verified) {
        this.patient = patient;
        this.guardian = guardian;
        this.verified = verified;
    }

}

이메일 인증

  1. POP3/IMAP 설정 -> 저번 포스팅에서 다룬 이메일인증을 위한 환경설정을 다뤘기 때문에 생략하도록 하겠습니다.
    이메일 인증 환경 설정

  2. EmailConfig
    EmailConfig는 이메일을 전송하기 위한 JavaMailSender를 설정합니다. @Configuration을 통해 이메일 서버 설정을 로드하고, JavaMailSender를 생성해 이메일 전송에 필요한 속성들을 정의합니다.

@Configuration
public class EmailConfig {
    @Value("${mail.host}")
    private String host;

    @Value("${mail.port}")
    private int port;

    @Value("${mail.username}")
    private String username;

    @Value("${mail.password}")
    private String password;

    @Value("${mail.auth}")
    private boolean auth;

    @Value("${mail.starttls.enable}")
    private boolean starttlsEnable;


    @Value("${mail.timeout}")
    private int timeout;


    @Bean
    public JavaMailSender javaMailSender(){
        JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
        mailSender.setHost(host);
        mailSender.setPort(port);
        mailSender.setUsername(username);
        mailSender.setPassword(password);
        mailSender.setDefaultEncoding("UTF-8");
        mailSender.setJavaMailProperties(getMailProperties());

        return mailSender;
    }

    private Properties getMailProperties(){
        Properties properties = new Properties();
        //"mail.smtp.auth": 이메일 서버가 사용자 인증을 필요로 하는지 여부를 설정 변수 auth의 값에 따라 true 또는 false가 될 수 있음.
        properties.put("mail.smtp.auth", auth);
        properties.put("mail.host",host);
        properties.put("mail.username",username);
        properties.put("mail.password",password);

        //"mail.smtp.starttls.enable": SMTP 서버가 STARTTLS 명령을 사용하여 보안 연결을 시작할 수 있는지 여부를 설정.. 변수 starttlsEnable에 의해 결정.
        properties.put("mail.enable", starttlsEnable);
        //"mail.smtp.timeout": 메일 서버로부터 응답을 기다리는 타임아웃 시간(밀리초 단위)을 설정. 변수 timeout에 지정된 값으로 설정.
        properties.put("mail.timeout", timeout);


        return properties;
    }

}
  1. 보호자 - 이메일 전송 전 환자 정보 확인

getPatientConnectingInfo 메서드는 보호자가 초대를 보내기 전에 환자의 이메일을 통해 환자 정보를 확인하는 기능을 제공합니다. 확인된 환자 정보는 세션에 저장됩니다.

    @Override
    public PatientConnectingResponse getPatientConnectingInfo(String email , HttpServletRequest request) {
        User user = jwtAuthenticationService.authenticationToken(request).get();
        log.info("[userEmail] : {}",user.getEmail());

        //환자 이메일
        Optional<User> patient = userDao.findUserByEmail(email);
        String patientEmail = patient.get().getEmail();
        PatientConnectingResponse response = userDao.findPatientConnecting(patientEmail);
        request.getSession().setAttribute("email", email);
        return response;
    }
  1. 보호자 - 이메일 전송
    sendInviteCode 메서드는 보호자가 환자에게 초대 코드를 포함한 이메일을 전송하는 기능을 담당합니다. 초대 코드를 생성한 후, 이메일로 발송하고 대기 상태로 데이터를 저장합니다.
 @Override
    public Map<String, String> sendInviteCode(HttpServletRequest request) throws Exception {
        Optional<User> user = jwtAuthenticationService.authenticationToken(request);
        String email = (String)request.getSession().getAttribute("email");
        Optional<User> patient = userDao.findUserByEmail(email);
        String ePw = make_InviteCode();

        MimeMessage message = createMessage(user.get().getUserName(),email,ePw);
        try{
            javaMailSender.send(message);
            ConnectionWaiting saveConnectionWaiting = new ConnectionWaiting(
                    patient.get().getUserName(),
                    patient.get().getEmail(),
                    ePw,
                    "대기중",
                    user.get().getEmail()
            );
            connectionWaitngDao.save(saveConnectionWaiting);
        }catch (MailException e){
            e.printStackTrace();
            throw new IllegalArgumentException();
        }

        Map<String,String>response = new HashMap<>();
        response.put("GuardianName",user.get().getUserName());
        response.put("Confirmation : ", ePw);

        return response;
    }
  1. 환자 - 인증코드 입력 후 연결
    confirmGuardianRelation 메서드는 환자가 받은 초대 코드를 입력하면 보호자와의 관계를 연결합니다. 초대 코드를 검증한 후, 보호자와 환자의 관계를 PatientGuardian 엔티티에 저장하고 상태를 업데이트합니다.
    @Override
    public ResultDto confirmGuardianRelation(String inviteCode, HttpServletRequest request) {
        ResultDto resultDto = new ResultDto();

        // ConnectionWaiting 엔티티에서 환자의 이메일과 초대 코드를 사용하여 정보 조회
        Optional<ConnectionWaiting> connectionWaitingOpt = connectionWaitingRepository.findByInviteCode(inviteCode);
        ConnectionWaiting connectionWaiting = connectionWaitingOpt.get();

        String patientEmail = connectionWaitingOpt.get().getPatientEmail();
        // 환자 정보 조회
        Optional<User> patientOpt = userRepository.findByEmail(patientEmail);
        User patient = patientOpt.get();

        // 보호자 이메일을 통해 보호자 정보 조회
        Optional<User> guardianOpt = userRepository.findByEmail(connectionWaiting.getGuardianEmail());
        User guardian = guardianOpt.get();


        if (!connectionWaitingOpt.isPresent()) {
            return createFailureResult(resultDto, "인증 코드가 올바르지 않습니다.");
        }

        if (!patientOpt.isPresent()) {
            return createFailureResult(resultDto, "환자를 찾을 수 없습니다.");
        }
        if (!guardianOpt.isPresent()) {
            return createFailureResult(resultDto, "보호자를 찾을 수 없습니다.");
        }


        // PatientGuardian 엔티티에 보호자-환자 관계 저장
        PatientGuardian patientGuardian = new PatientGuardian(
                patient, guardian, true);

        patientGuardianDao.save(patientGuardian);
        connectionWaiting.setStatus("승인");
        connectionWaitngDao.save(connectionWaiting);

        resultDto.setDetailMessage("보호자와 환자의 관계가 성공적으로 연결되었습니다.");
        resultDto.setSuccess(true);

        return resultDto;
    }

결과 확인

iywrD1R
1. 보호자 - 환자 정보 확인하기(유저 1)

2.보호자 - 이메일 전송하기
보호자가 이메일을 전송하면 테스트로 인증번호와 보호자이름을 반환하도록 하였습니다.

3. 환자 - 이메일 확인하기 (유저 2)
아래 사진과 같이 환자의 이메일로 인증코드가 온 것을 확인할 수 있습니다.

4. 인증번호 검증 및 연결하기
이렇게 연결이 된 것을 확인할 수 있습니다.

데이터베이스 확인하기

대기 데이터 저장 테이블과 보호자-환자 테이블에 값이 잘 저장되었습니다.

결론

이메일 인증으로 쉽게 환자-보호자 연결 기능을 만들었지만, 굳이 이메일이 아니더라도 다른 방법을 사용하여 사용자 연결을 쉽게 할 수 있을 것 같습니다.
추후에 인증코드 관련 데이터 테이블은 레디스로 관리하도록 디벨롭 할 예정입니다.

profile
Development Record Page

0개의 댓글