WMS 창고 물류관리 시스템
- 22.10.24 ~ 22.11.24 (4주)
- 팀프로젝트(8명)
- Java 11
- Spring
- maven
- AWS EC2, AWS RDS , Jenkins, Github-Webhook, Oracle Cloud, Ngrok
- Oracle
- Jsp
- Tomcat
- Html, Css, Javascript, Jquery, bootstrap
- RestAPI
- Generic
- WebSoket
- Cookies
- Service
이 서비스의 핵심 기능은 창고재고관리 및 채팅입니다.
사용자는 이 서비스를 이용하기 위해 기초 정보를 등록하고 그 등록한 정보를 토대로 재고를 발주,수주,이동을 통하여 재고관리를 할 수 있습니다.
핵심 기능 설명 펼치기
-
이전 프로젝트에서는 Spring에서 War 파일을 추출하여 FileZilla로 프로젝트를 배포하였으나 이렇게 진행할 시
관리자는 매번 프로젝트를 다시 올려야하고 사용자는 관리자가 배포하는 기간동안 이용할 수 없는 번거럽고 치명적인 문제점 발생 -
이것을 해결하기 위해 AWS EC2에 Jenkins를 설치하여 사용자는 서버1을 사용하고 있다가 관리자가 Github에 Push할 때마다 Github_Webhook로 신호를 보내주고 그 신호를 받은 Jenkins가 서버2에 자동적으로 배포를 하고 배포가 완료됨과 동시 사용자는 서버2를 사용하게 되는 것입니다.
-
이로 인해 관리자는 지속적인 배포의 번거로움을 없애고 사용자는 끊김없는 서버를 사용할 수 있게 됩니다.
[ 블로그 정리 ]
-
이전 프로젝트에서는 Spring의 버전이 낮아 WebSoket을 사용하기 적절하지 않아 Ajax를 통한 reload 새로고침을 사용하여 구현하였습니다.
-
이번 프로젝트에서는 구현을 시작하기 앞서 Spring의 버전을 높히고 Websoket을 활용하였습니다.
-
Websoket을 활용하여 새로고침없는 (끊김없는) 실시간 채팅을 구현하였습니다.
create or replace FUNCTION GENERATE_LOT
(V_ITEM_NO ITEM.NO%TYPE)
RETURN VARCHAR
IS
V_LOT_CODE LOT.CODE%TYPE; -- 가장 최근 로트코드
VR_LOT_CODE LOT.CODE%TYPE; -- 만들어진 로트코드
V2_ITEM_NO ITEM.NO%TYPE; -- 가장 최근 로트코드의 품목번호
V_INPUT_DATE VARCHAR2(8);
V_SEQ VARCHAR2(3);
BEGIN
WITH A AS (
SELECT NO, CODE, ITEM_NO, REG_DATE, ROW_NUMBER() OVER (ORDER BY NO DESC) AS LEV
FROM LOT
)
SELECT CODE
INTO V_LOT_CODE
FROM A
WHERE LEV = 1;
V_INPUT_DATE := SUBSTR(V_LOT_CODE, 0, 8);
V_SEQ := SUBSTR(V_LOT_CODE, -3);
IF TO_CHAR(SYSDATE, 'YYYYMMDD') != V_INPUT_DATE THEN
V_INPUT_DATE := TO_CHAR(SYSDATE, 'YYYYMMDD');
V_SEQ := LPAD('1', 3, '0');
ELSE
V_SEQ := LPAD(TO_NUMBER(V_SEQ) + 1, 3, '0');
END IF;
VR_LOT_CODE := V_INPUT_DATE || V_ITEM_NO || V_SEQ;
RETURN VR_LOT_CODE;
END;
- 이번 프로젝트에서는 상품마다 부여되는 로트번호를 생성하기 위해 함수를 사용하여 같은 코드를 여러번 반복하지 않고 간결하게 보다 Clean한 코드를 작성하였습니다.
@GetMapping(
value={"/pages/{pageNum}/{amount}", "/pages/{pageNum}/{amount}/{whatColumn}", "/pages/{pageNum}/{amount}/{whatColumn}/{keyword}"},
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<PageDTO<ItemVO>> clientlist(
@PathVariable("pageNum") int pageNum,
@PathVariable("amount") int amount,
@PathVariable(value="whatColumn", required = false) String whatColumn,
@PathVariable(value="keyword", required = false) String keyword) {
//System.out.println(pageNum + " " + amount + " " + whatColumn + " " + keyword);
Criteria cri = new Criteria(pageNum, amount, whatColumn, keyword);
return new ResponseEntity<>(service.getListPage(cri), HttpStatus.OK);
}
- Rest API를 사용하여 메시지를 읽는 것 만으로도 메시지가 의도하는 바를 명확하게 파악할 수 있도록 진행하였습니다.
@Data
@AllArgsConstructor
public class PageForWareHouseDTO<T> {
private int totalCount;
private List<T> list;
private CriteriaForWareHouse cri;
}
- Generic 방식을 사용하여 비슷한 코드의 재사용성을 높혔으며, 후에 관리하기가 쉽도록 구현하였습니다.
기존 코드
@RequestMapping(value = command , method = RequestMethod.POST)
public String updateMember(MemberBean mb,Model model) {
return "redirect:mypage.mb?select=6";
}
개선된 코드
@PostMapping("/update")
public String update(ClientVO VO,SearchVO searchvo,RedirectAttributes rttr) {
service.update(VO);
rttr.addFlashAttribute("searchvo",searchvo);
return redirect;
}
-
이전 프로젝트에서는 변수를 하나하나 받아와서 redirect 주소 뒤에 변수를 붙혀 넘기는 방식을 사용했었습니다.
-
이번 프로젝트에서는 개선하여 RedirectAttributes클래스를 사용하여 사용할 변수들을 객체에 담아 객체를 바로 넘길 수 있도록 하여 Clean한 코드를 작성하였습니다.
기존 코드
$.ajax({
type : 'post',
url : "allchatting.mb",
contentType: "application/x-www-form-urlencoded; charset=UTF-8",
success : function(data) {
var roomlist = data.split("|");
for(var i=0 in roomlist){
var roomlists = roomlist[i].split(",");
$('#lists').append(
'<div class="card-body navbar-light px-0" data-simplebar>'+
'<div class="navbar-nav">'+
'<a onClick="detailmsg('+roomlists[1]+')" class="nav-link d-flex align-items-center px-3 gap-3">'+
'</a>'+
'</div>'+
'</div>'+
'</div>');
}
}//else
}//success
})//ajax
개선된 코드
$.getJSON("/chat/getAll",
function(c){
for(i=0;i<c.length;i++){
$("#messageArea").append(
"<div class='chat ch1'>"+
"<div class='lnamed'>"+c[i].member_name+" "+c[i].rank_name+"</div><div class='textbox'>"+c[i].content+"</div></div>");
}
)
-
이전 프로젝트에서는 Controller에서 객체를 문자열로 바꿔준 후 view에서 Split으로 List로 만들어 준 후 사용을 하여 번거럽고 효율적이지 못한 코드 작성이였습니다.
-
이번 프로젝트에서는 개선하여 Controller에서 바로 Json형태로 받은 후 바로 그 Json에 담긴 변수를 사용할 수 있도록 개선하여 코드의 효율성을 높혔습니다.
- 이전 프로젝트에서 로그인 후 화면 이동 시 일정시간이 지나면 세션이 풀려 에러가 발생하는 문제 발생
- 이번 프로젝트에서는 세션이 풀리면 알림창을 뜨게 하여 에러 발생을 방지하였습니다.
-
이전 프로젝트에서 한 Controller에서 여러 작업을 하여 복잡하고 알아보기 힘든 코드로 작성이 되었었습니다.
-
이번 프로젝트에서는 Service 방식을 활용하여 Controller에서 한가지의 작업을 하면 뒤에 있는 Service에서 한 번에 작업을 하여 Clean 한 코드를 작성하였습니다.
SELECT * FROM client where email like '%' || #{keyword} || '%'
ORDER BY no desc OFFSET #{beginRow} ROWS FETCH NEXT #{pageSize} ROWS ONLY
- 다른 페이징 방식보다 빠르고 간편한 페이징, 속도가 가장 빨라서 효율적인 방식으로 코드를 작성하였습니다.
-
모든 프로젝트를 마무리하고 로컬에서의 테스트를 문제없이 진행하고 AWS에 배포를 하여 테스트를 하는데
배포할 때만 발생하는 문제를 발견하게 됐습니다. -
AWS의 EC2의 Unbuntu Lang은 UTF-8 형식을 사용하여 프로젝트에서 사용하는 ko_KR 형식과 달라 날짜 형식을 읽지 못하여 발생하는 문제였습니다.
-
이전 프로젝트에서는 프로젝트의 모든 Date 타입을 쓴 sql문들을 UTF-8 형식으로 바꿔주는 번거러운 작업을 하였습니다.
-
이번 프로젝트에서는 번거러운 작업을 방지하기위해 AWS EC2의 Lang을 oracle sql에 맞는 ko_KR 로 바꿔주었습니다.
[ 블로그 정리 ]
-
Jenkins에서 배포 시 특정 부분도 아닌 계속 여러 부분에서 멈추는 현상이 발생하였습니다.
-
Jenkins를 구동하기 위한 최소한의 사양이 있는데 AWS EC2의 무료 버전인 micro를 사용하게 되면 Ram 용량이 부족하게 되는 현상을 발견하였습니다.
-
이를 해결하기위해 EC2 내에 swap 공간을 활용하여 Ram 사양을 키운 듯한 효과를 내었습니다.
[ 블로그 정리 ]
배포 관련 에러
Jenkins UTF-8 에러
Jenkins 빌드 중 maven encoding 에 문제가 발생하였습니다.[ 블로그 정리 ]
비용 절약
AWS RDS는 비용이 부담되는 문제가 있어서 Oracle Cloud와 Local을 활용할 수 있는 Ngrok을 활용하여 비용 절약을 하였습니다. [ Ngrok 정리 ] [ Oracle Cloud 정리 ]
Oracle Cloud 연동 시 ojdbc 에러
이전에 사용하던 ojdbc 버전과 현재 ojdbc 버전이 달라서 발생하는 문제여서 pom.xml 수정을 통해 해결을 해결하였습니다. [ 블로그 정리 ]
Github Webhook 404 에러
github에 push시 자동 빌드화가 되도록 설정을 하였는데 빌드도 되지 않을 뿐더러 Webhook에서 404 에러가 발생하여 URL 수정 후 해결하였습니다. [ 블로그 정리 ]
Oracle Cloud 연동 실패
Oracle Cloud DB는 등록된 IP가 아니면 접속이 안되게 막아두었습니다. 이를 통해 IP를 등록한 후 DB 접속이 가능하도록 하였습니다. [ 블로그 정리 ]
Build 중 Root 에러
Jenkins Build 중 Tomcat의 Root를 지우지 못한다는 에러가 발생하여 Root 폴더를 임의의 이름으로 수정을 해결하였습니다. [ 블로그 정리 ]
Pom 경로 문제
Jenkins Build 중 Pom의 경로를 찾지 못하여 몇 번의 수정을 통해 경로를 찾아 수정하고 해결하였습니다. [ 블로그 정리 ]
jar Build 후 UTF-8
Jenkins Build 중 Encoding이 설정이 되어 있지 않다는 에러를 마주한 후 Pom.xml에 Encoding 설정 후 해결하였습니다. [ 블로그 정리 ]
그 외 에러
Mybatis 변수 에러
sql문 작성 시 변수 하나는 인식을 하지 못하므로 변수 하나일 시 Value로 작성해주어 해결하였습니다. [ 블로그 정리 ]
No mapping Found 에러
Controller 와 xml 문제가 없었는데도 해당 에러가 발생하여서 Java Build Path ojdbc 경로를 수정한 후 해결하였습니다. [ 블로그 정리 ]
Tomcat already in use 에러
다른 곳에서 8080 포트를 이미 사용하고 있어서 발생하는 문제였습니다. 포트를 임의로 8081로 수정한 후 해결하였습니다. [ 블로그 정리 ]
프로젝트 개발 회고 :
- 이전 프로젝트보다 훨씬 효율적이고 Clean 한 코드를 작성할 수 있어 발전할 수 있었습니다. 다음도 그 다음도 전보다 훨씬 나은 코드 작성을 할 것입니다.
- 팀원 모두가 팀프로젝트를 통하여 하나라도 더 얻어갈 수 있도록 서로 돕고 지원하였습니다.
- 다양한 방법을 하고 싶었고 처음 접하는 것들이 많아 에러가 무수히 발생하였지만 더 많은 것들을 배울 수 있었기에 재미있었습니다.
- 사용자 입장에서 사용해보고 불편한 점이 있는 지 확인해가면서 프로젝트를 진행하였습니다.
- 이번에 사용한 기술을 다음 프로젝트에서는 더욱 효율적으로 코드를 작성할 수 있는 방법이 있는 지 알아보고 배워가며 적용해보고 싶습니다.
- 이번 프로젝트에서는 팀장 역할을 맡게 되어 모든 이들이 하나라도 더 얻어갈 수 있도록 도움을 주고 팀의 좋은 분위기를 형성하였습니다.
- 팀원 각자에게 업무를 주고 맡은 업무 외에도 서로 돕고 지원하였습니다.