Ajax 비동기 요청 발생시 로딩 바 만들기.
지금 만들고 있는 프로젝트를 SPA(Single Page Application ) 으로 만들고 있습니다.
동기 방식의 페이지 이동을 하는 웹 어플리케이션을 만들었을때와는 다르게 세세히 신경써야 하는 부분이 굉장히 많은데요,
그 중에는 수업시간에서 따로 다룬 적 없는 부분도 종종 있었습니다.
몇가지 예를 들자면,
1. 페이지 이동시 url 변경 시키기
-> history.pushState 함수를 이용해 해결 했습니다.
2. 뒤로가기 이벤트 발생시 처리
-> pushState 발생시 data에 기록해둔 데이터를 바탕으로 $(window).bind("popstate", function(event){} 로 뒤로가기에 대한 바인딩을 해 해결 했습니다.
3. 페이지 이동시 url 을 변경 시켜 뒀는데, 그 url 상태에서 새로 고침시 페이지 해당 페이지 그대로 보여주기
package best.gaia.project.controller;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.WebApplicationContext;
import best.gaia.project.dao.ProjectDao;
import best.gaia.project.service.ProjectService;
import best.gaia.utils.CookieUtil;
import best.gaia.utils.exception.ResourceNotFoundException;
import static best.gaia.utils.SessionUtil.*;
@Controller
@RequestMapping("{manager_id:^(?:(?!admin$|view$|restapi$).)*$}/{project_title:^(?:(?!new$|overview$|help$|setting$|activity$).)*$}")
public class ProjectUrlMapper {
@Inject
private ProjectService service;
@Inject
private ProjectDao dao;
@Inject
private WebApplicationContext container;
private ServletContext application;
@PostConstruct
public void init() {
application = container.getServletContext();
}
private static final Logger logger = LoggerFactory.getLogger(ProjectUrlMapper.class);
@GetMapping({""
,"{pageParam}"
,"issue/{issue_no}"
,"milestone/{milest_no}"
})
public String projectMenuOverview(
@PathVariable String manager_id
,@PathVariable String project_title
,@PathVariable Optional<String> pageParam
,@PathVariable Optional<String> issue_no
,@PathVariable Optional<String> milest_no
,Authentication authentication
,HttpSession session
,Model model
,HttpServletResponse resp
) throws UnsupportedEncodingException {
// manager_id랑 project_title로 proj_no 찾아 내기
Map<String, Object> map = new HashMap<>();
map.put("manager_id", manager_id);
map.put("project_title", project_title);
Integer proj_no = dao.getProjNoFromIdAndTitle(map);
// 존재하는 프로젝트 인지 검사 후 존재하지 않으면 404 에러 응답.
if(proj_no == null)
throw new ResourceNotFoundException();
// 접속중인 유저가 해당 proj_no에 대해 조회할 수 있는 권한이 있는지 체크
int mem_no = getMemberNoFromAuthentication(authentication);
/* 코드 작성 필요*/
// 조회중인 프로젝트의 proj_no 를 세션에 저장하기
session.setAttribute("proj_no", proj_no);
// Cookie 에 접속중인 회원의 proj 내에서의 닉네임을 쿠키에 저장하기
String proj_user_nick = service.getProjectNick(proj_no, mem_no);
CookieUtil.addCookie("proj_user_nick", proj_user_nick, resp);
// pageParam 없는 요소들은 수동으로 pageParam 넣어주기.
// 매핑 패턴을 {pageParam}/{paramNo}하고 paramNo도 Optional로 받으면 하드코딩 하지 않아도 될듯.
if(issue_no.isPresent()) {
if("new".equals(issue_no.get())) {
pageParam = Optional.of("issue/new");
}else {
pageParam = Optional.of("issueview");
}
}else if(milest_no.isPresent()) {
if("new".equals(milest_no.get())) {
pageParam = Optional.of("milestone/new");
}else {
pageParam = Optional.of("milestoneview");
}
}
if(!pageParam.isPresent())
pageParam = Optional.of("code");
model.addAttribute("manager_id", manager_id);
model.addAttribute("project_title", project_title);
model.addAttribute("pageParam", pageParam.get());
model.addAttribute("issue_no", issue_no.isPresent() ? issue_no.get() : null);
model.addAttribute("milest_no", milest_no.isPresent() ? milest_no.get() : null);
return "view/template/project";
}
}
> 위에 작성한 컨트롤러가 모두 잡아낸다음 요청을 분석해 보내도록 해서 해결 했습니다. issue/ 와 milestone/ 쪽은 {pageParam}/{numParam} 이런식으로 해결하면 조금 더 코드가 간결해질텐데 워낙 일이 밀려 아직 변경하지 못했습니다.
이 외에도 여러가지 고려 사항과 고민이 많았는데요, 그 중 하나인 페이지 이동시 로딩 바를 보여주는 기능에 대해 기술 해 보도록 하겠습니다.
위의 영상에서 보이는 것 처럼, 비동기 이동시에는 사용자 입장에서 화면에 변화가 즉각 즉각 일어나지 않으면, 본인의 요청에 대해 응답이 없다고 느껴질 수 있는데요. 영상에서는 그나마 빠릿 빠릿 했는데, 요청에 대한 응답 데이터가 크거나, WAS 서버와 DB 서버 둘중 하나라도 느려지는 상황이 발생하면 UX 적인 측면에서 굉장히 불편함을 느낄 수 있습니다.
1. http://www.ajaxload.info/ 에서 원하는 로딩 이미지를 선택해 생성 하고 다운 받습니다. License는 WTFPL 라이센스를 따르고 있으니 편하게 사용하시면 됩니다.
Ajaxload - Ajax loading gif generator
Ajaxload (Beta) <- Hey ! This service is Web 2.0 Preview Create easily your own ajax loader icon : Select the type of indicator you want Enter the background code color you want (tick "Transparent background" if you don't want one Enter the foreground code
www.ajaxload.info
2. document가 준비 되면 로딩 화면 돔을 생성 하고 body 에 넣도록 지정을 해 줍니다.
jquery를 사용한다면 , $function(){} 안에 넣어야 준비가 되었는지를 알 수 있습니다.
vanilla javascript를 사용한다면
document.addEventListener("DOMContentLoaded", function() {
// code...
});
를 이용 하시면 됩니다.
$(function(){
let loading = $('<div id="loading" class="loading"><img id="loading_img" alt="loading" src="/resources/images/loading/ajax-loader.gif" /></div>')
.appendTo(document.body).hide();
$(window).ajaxStart(function(){
loading.show();
}).ajaxStop(function(){
loading.hide();
});
})
loading 이라는 돔을 생성 한 뒤, document.body 에 append 시키고 hide 합니다.
그 후에 ajaxStart와 ajaxStop 함수에 .show와 .hide 를 걸어줍니다.
이번에는 css 설정도 해줍니다. loading_img의 height 를 조절하면 로딩이미지 크기를 조절 할 수 있습니다. margin-top 와 left에는 해당 크기를 그대로 넣어줘야 중앙에 위치하도록 할 수 있습니다.
@charset "utf-8";
/* 로딩 관관 css */
#loading {
height: 100%;
left: 0px;
position: fixed;
_position:absolute;
top: 0px;
width: 100%;
filter:alpha(opacity=50);
-moz-opacity:0.5;
opacity : 0.5;
}
.loading {
z-index: 100;
}
#loading_img{
position:absolute;
top:50%;
left:50%;
height:100px;
margin-top:-100px;
margin-left:-100px;
z-index: 200;
}
이후 실행을 시켜보았습니다.
비동기 요청으로 ajax 함수가 실행되는 동안에는 중간에서 로딩 바가 돌기 시작했습니다.
.loading에 background-color 를 주면 로딩 시에 화면을 어둡게 하는 효과가 가능한데요, 그러면 저희는 싱글 페이지의 정체성이 줄어들게 되어서 그렇게 하지 않았습니다.
로딩 바 적용 후 영상입니다.
이상입니다.
'Development > Projects-DDIT' 카테고리의 다른 글
최종 프로젝트 GAIA 소개 (0) | 2021.07.18 |
---|---|
Google Analytics 데이터 java 통해 받아오기 (1) | 2021.06.22 |
Github REST API 사용하기 (1) | 2021.06.11 |
GAIA 알람 시스템을 만들기 위해 구축한 여러가지 모듈 소개와 과정 (0) | 2021.06.09 |
Google Analytics 구글 애널리틱스 활용하기 - 웹 어플리케이션에 적용 (3) | 2021.05.23 |