예약 프로세스
- 홈페이지에서 '검진 예약' 선택 → 패키지 목록 페이지 이동
- 패키지 선택 → 상세 정보 페이지 이동
- '즉시 예약' 클릭 → 예약 페이지 이동
- 휴대폰 번호 입력 → 인증번호 전송
- 인증번호 입력 → 예약 완료
예약 페이지 구현
패키지 정보 표시
<script>
const packageId = new URL(location.href).searchParams.get("id");
const app = new Vue({
el: '#app',
data: {
packageData: {},
reservation: {
packageId: packageId,
gender: 'M'
}
},
mounted() {
axios.get(`/packages/${packageId}`)
.then(response => this.packageData = response.data);
}
});
</script>
휴대폰 인증
function validatePhone(phone) {
return /^1[3-8]\d{9}$/.test(phone);
}
// Vue 메서드
sendAuthCode() {
if (!validatePhone(this.reservation.phone)) {
alert('유효한 휴대폰 번호를 입력하세요');
return;
}
// 인증번호 전송 및 타이머 로직
}
날짜 선택기
<script>
const dateSelector = new Calendar({
trigger: '.date-picker',
minDate: addDays(new Date(), 1),
maxDate: addDays(new Date(), 30)
});
function addDays(date, days) {
const result = new Date(date);
result.setDate(result.getDate() + days);
return result;
}
</script>
예약 요청
submitReservation() {
axios.post('/reservations', this.reservation)
.then(response => {
if (response.data.success) {
location.href = `/success?id=${response.data.id}`;
}
});
}
백엔드 처리
인증번호 컨트롤러
@RestController
@RequestMapping("/auth-codes")
public class AuthCodeController {
@PostMapping("/send")
public ResponseEntity<?> sendCode(@RequestParam String phone) {
String code = generateCode(4);
redisTemplate.opsForValue().set(
phone + "_RESERVE",
code,
5, TimeUnit.MINUTES
);
// SMS 전송 로직
return ResponseEntity.ok().build();
}
}
예약 컨트롤러
@PostMapping("/reservations")
public ResponseEntity<ReservationResult> createReservation(
@RequestBody ReservationRequest request) {
String storedCode = redisTemplate.opsForValue()
.get(request.getPhone() + "_RESERVE");
if (!request.getAuthCode().equals(storedCode)) {
return ResponseEntity.badRequest().build();
}
ReservationService.Result result = reservationService
.processReservation(request);
return ResponseEntity.ok(result);
}
예약 서비스
@Service
public class ReservationService {
public Result processReservation(ReservationRequest request) {
Date selectedDate = parseDate(request.getDate());
Schedule schedule = scheduleRepo.findByDate(selectedDate);
if (schedule == null || schedule.isFull()) {
return new Result(false, "예약 불가");
}
Member member = memberRepo.findByPhone(request.getPhone());
if (member == null) {
member = registerNewMember(request);
}
if (hasDuplicateReservation(member, request)) {
return new Result(false, "중복 예약");
}
Reservation newReservation = createReservation(member, request);
schedule.incrementReservations();
return new Result(true, newReservation.getId());
}
}
예약 완료 페이지
<script>
const app = new Vue({
data: { reservationDetails: {} },
created() {
axios.get(`/reservations/${getUrlParam('id')}`)
.then(res => this.reservationDetails = res.data);
}
});
</script>
<div>
<p>예약자: {{reservationDetails.memberName}}</p>
<p>패키지: {{reservationDetails.packageName}}</p>
<p>예약일: {{reservationDetails.date}}</p>
</div>
예약 상세 조회
@GetMapping("/reservations/{id}")
public Map<String, Object> getReservationDetails(
@PathVariable int id) {
return reservationRepo.findDetailsById(id)
.orElseThrow(NotFoundException::new);
}