location.reload(true);
를 사용하여 구현했다.(구독 시 광고 제거)
IMP.init('imp개인코드');
IMP.request_pay({
pg: "inicis",
pay_method: "card",
merchant_uid : 'merchant_'+new Date().getTime(),
name : 'moneyMate',
amount : parseInt(realPrice.value),
buyer_email : memberEmail,
buyer_name : memberNickname,
}, function (rsp) { // callback
if (rsp.success) {
// 결제 성공 시 로직,
var msg = '결제가 완료되었습니다.';
msg += '고유ID : ' + rsp.imp_uid;
msg += '상점 거래ID : ' + rsp.merchant_uid;
msg += '결제 금액 : ' + rsp.paid_amount;
msg += '카드 승인번호 : ' + rsp.apply_num;
const data = { "amount" : realPrice.value,
"useMile" : useMile1.value,
"prePrice" : prePrice.value };
fetch("/subscribe/calculate/kg", {
method : "POST",
headers : {"Content-Type" : "application/json"},
body : JSON.stringify(data)
})
.then(resp => resp.text())
.then( result => {
if(result > 0 ){
location.href = "/subscribe/end?no=" + result;
} else {
alert("결제에 실패하셨습니다.");
location.href = "/";
}
})
.catch(err => {
console.log(err);
});
} else {
// 결제 실패 시 로직,
var msg = '결제에 실패하였습니다.';
msg += '에러내용 : ' + rsp.error_msg;
}
😂
생각보다 JS 처리를 꼼꼼히 해야 하는 작업이었다.
마일리지에 숫자가 아닌 것을 입력한 경우, 상품금액이 마일리지보다 적은데 마일리지 전액 사용을 누른 경우, 마일리지를 사용해 0원 결제한 경우 등등등..
벡엔드에서도 여러 테이블들의 값을 변경하다보니 수정을 반복했던 작업
<select id="gList" resultMap="account_rm">
SELECT ACNT_NO, ACNT_NAME, (SELECT LISTAGG(MEMBER_EMAIL, ',,') WITHIN GROUP (ORDER BY MEMBER_EMAIL)
FROM ACCOUNT_GROUP
WHERE 1=1
AND ACNT_NO IN (SELECT ACNT_NO
FROM ACCOUNT_GROUP
WHERE MEMBER_EMAIL = #{memberEmail}
)
AND GROUP_FL = 'Y') MEMBER_EMAILS
FROM ACCOUNT_GROUP
JOIN ACCOUNTBOOK USING (ACNT_NO)
WHERE MEMBER_EMAIL = #{memberEmail}
GROUP BY ACNT_NO, ACNT_NAME
ORDER BY ACNT_NO
</select>
MoneyMate회원이 아닌 사람의 이메일, 본인 이메일, 이미 입력되어 있는 이메일은 추가할 수 없게 했다.
추가 후 X버튼 클릭 시 삭제되게 했다.
반복문을 사용하여 초대된 회원들에게 초대장 이메일이 전송되도록 했다.
for(String email : gEmail) {
String authKey = createAuthKey();
//인증메일 보내기
MimeMessage mail = mailSender.createMimeMessage();
// 제목
String subject = "[MoneyMate] 가계부 초대";
// 문자 인코딩
String charset = "UTF-8";
// 메일 내용
String mailContent
= "<h1>[MoneyMate] 가계부 초대 링크입니다.</h1>"
+ "<button style=\"width: 120px; height: 30px; background-color: lightblue; border: 0; \">"
+ "<a href='http://localhost/accounted/invite/" + authKey + "'"
+ "style='text-decoration: none; color: black; font-weight: bold;'"
+">"
+ "초대장 바로가기</a>"
+ "</button>";
// 보내는 사람 지정
mail.setFrom(new InternetAddress("개인정보@gmail.com", "MoneyMate"));
mail.addRecipient(Message.RecipientType.TO, new InternetAddress(email));
// 이메일 제목
mail.setSubject(subject, charset);
// 내용
mail.setText(mailContent, charset, "html");
mailSender.send(mail);
// 그룹 초대 테이블에 insert하기
account.setMemberEmail(email);
account.setAuthKey(authKey);
if(result>0) {
result = dao.group(account);
}
😂
이메일로 그룹초대하는 로직은 생각보다 어렵지 않았는데, 만만하게 봤던 가계부 목록조회가 까다로웠다. 그룹 가계부의 경우 그룹 멤버들의 이메일도 같이 조회되게끔 했다.
(본인 글에 댓글이 달린 경우, 본인 댓글에 대댓글이 달린 경우 실시간 알림 구현)
움짤 속 처음 알림의 개수는 1개인데 댓글이 달리자 바로 2개로 바뀌었다.
<beans:bean id="alertHandler" class="edu.kh.project.alert.model.websocket.AlertWebsocketHandler"/>
<websocket:handlers>
<websocket:mapping handler="alertHandler" path="/alertSock"/>
<websocket:handshake-interceptors>
<beans:bean class="org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor"/>
</websocket:handshake-interceptors>
<websocket:sockjs/>
</websocket:handlers>
let alertSock;
alertSock = new SockJS("/alertSock");
@Autowired
private AlertWebsocketHandler handler;
handler.sendNotificationToClients(comment.getCommentNo());
public void sendNotificationToClients(int commentNo) {
CBoard board = service.memberNo(commentNo);
for(WebSocketSession s : sessions) {
int userNo = ((Member)s.getAttributes().get("loginMember")).getMemberNo();
if(userNo == board.getBMemberNo()) {
try {
// 그 사람의 알림함 조회
List<Alert> aList = service.bAList(board.getBMemberNo());
// 안 읽은 알림 개수 조회
int count = service.bCount(board.getBMemberNo());
Map<String, Object> map = new HashMap<String, Object>();
map.put("aList", aList);
map.put("count", count);
s.sendMessage(new TextMessage(new Gson().toJson(map)));
} catch (IOException e) {
e.printStackTrace();
}
}
if(board.getCMemberNo() != 0) {
if(userNo == board.getCMemberNo()) {
try {
// 그 사람의 알림함 조회
List<Alert> aList = service.bAList(board.getCMemberNo());
// 안 읽은 알림 개수 조회
int count = service.bCount(board.getCMemberNo());
Map<String, Object> map = new HashMap<String, Object>();
map.put("aList", aList);
map.put("count", count);
s.sendMessage(new TextMessage(new Gson().toJson(map)));
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
alertSock.onmessage = e => {
var map = JSON.parse(e.data);
alarmCount.innerText = map.count;
//console.log(map);
if(window.getComputedStyle(aPage).display === "block"){ // 알림함 열려있을 경우
selectAlarmList(map.aList);
} else {
alarmCount.style.display = "block"
selectAlarmList(map.aList);
}
}
😂
boot가 아닌 Spring을 이용한 실시간 알림 구현 예제가 많이 없어서 초반에 헤맸었다. 스케줄러, setInterval 등을 사용할까 고민했지만, 실시간 알림 구현을 완벽하게 해내기 위해 웹소켓을 사용했다.
실시간 알림 구현은 요구사항 정의서, ERD 작업을 할 때에도 할까말까 고민했던 기능이라 다른 기능을 다 마친 후 마지막에 하게 되었는데 이게 독이었다.
처음부터 웹소켓을 공부하고, 댓글을 insert 작업부터 핸들러에서 처리했다면 더 편했겠지만, 마지막에 하려니 모든 코드를 뒤엎어야 하는 상황이라 막막했다. Service에서 핸들러 메소드를 호출하는 방식이 잘 적용되어서 다행히 기존 코드 수정 없이 해낼 수 있었다.
(좋아요, 댓글 CRUD_비동기로 파일 첨부)
var form = $('#updateFrm')[0];
var formData = new FormData(form);
formData.append('commentNo', commentNo);
$.ajax({
type:"post",
enctype:'multipart/form-data',
url:'/event/account/update',
data:formData,
dataType:'json',
processData:false,
contentType:false,
cache:false,
success:function(result){
console.log("success : ", result);
alert("수정되었습니다.")
selectList();
},
error:function(e){
console.log("error : ", e);
}
});
💖좋았던 점
주제 선정부터 팀원들의 합이 잘 맞았다. 큰 관계가 있는 건 아니지만..나는 고등학생 때부터 모임에서 총무역할을 해왔기 때문에 가계부 사이트를 하자는 의견이 나왔을 때 반가웠다. 이외에도 금융에 관심이 있는 조원도 있었고, 세미 때와는 차별점을 두어 남들이 많이 하는 쇼핑몰과 같은 사이트를 피하자는 조원도 있었다.
프로젝트를 진행하면서도 큰 갈등 없이, 의견 충돌이 있을 경우에는 서로의 의견을 들어보며 더 나은 의견에 투표하는 방식으로 해결했다.
개인적으로 세미 프로젝트 때 API를 많이 활용하지 못 한 것이 아쉬웠었는데 이번 파이널 프로젝트를 통해 API를 활용한 기술들을 구현할 수 있어서 기뻤다.
💖아쉬웠던 점
제일 아쉬운 점은 역시 배포를 못한 것이다. 배포 관련해서 더 공부를 해서 배포까지 완성하고 싶다.