Scenario: 이미지 파일첨부를 하려는데 기존 소스는 다중 파일첨부 였다, (multipartHttpServletRequest 리스트를 받아서 리스트를 돌리는 구조의 코드였음.) 이것을 살짝 변형 시켜서 단일 파일첨부구조와 재활용성이 가능하게 바꾸려고 고쳐보았다.


Solutions: 다음



1. 가장먼저 Multipart 객체를 받아서 파일과, 함께보내진 폼 요소를 파싱해줄  자바의 파일유틸부터 만들어줌

  1.     //파일 하나만 업로드시킬려고만듬 FileUtil.java (클래스가 없을경우 만들어 줘야함)
  2.     public Map<String,Object> parseInsertFileInfoOne(Map<String,Object> map, HttpServletRequest request) throws Exception{
  3.        
  4.         MultipartHttpServletRequest multipartHttpServletRequest = (MultipartHttpServletRequest)request;
  5.         Iterator<String> iterator = multipartHttpServletRequest.getFileNames();
  6.        
  7.         MultipartFile multipartFile = null;
  8.         String originalFileName = null;
  9.         String originalFileExtension = null;
  10.         String storedFileName = null;
  11.        
  12.         Map<String, Object> FileInfoMap = null;
  13.        
  14.        
  15.         //폴더가 없으면 해당 폴더 생성
  16.         File file = new File(CommonUtils.filePath);
  17.         if(file.exists() == false){
  18.             file.mkdirs();
  19.         }
  20.        
  21.         while(iterator.hasNext()){
  22.             multipartFile = multipartHttpServletRequest.getFile(iterator.next());
  23.             if(multipartFile.isEmpty() == false){
  24.                 originalFileName = multipartFile.getOriginalFilename();
  25.                 originalFileExtension = originalFileName.substring(originalFileName.lastIndexOf("."));
  26.                 storedFileName = CommonUtils.getRandomString() + originalFileExtension;
  27.                
  28.                 file = new File(CommonUtils.filePath + storedFileName);
  29.                 multipartFile.transferTo(file);
  30.                
  31.                 FileInfoMap = new HashMap<String,Object>();
  32.                 //인터셉트 맵 자체를 리턴해줄 파일맵에다 넣어줌. 그렇다면 구지 뉴 객체를 생성안해도 될듯
  33.                 FileInfoMap = map;
  34.                
  35.                 FileInfoMap.put("ORIGINAL_FILE_NAME", originalFileName);
  36.                 FileInfoMap.put("STORED_FILE_NAME", storedFileName);
  37.                 FileInfoMap.put("FILE_SIZE", multipartFile.getSize());
  38. //              FileInfoMap.put("REFER_IDX", (String)map.get("IDX"));
  39. //              FileInfoMap.put("NAME", (String)map.get("NAME"));
  40. //              FileInfoMap.put("PJT_SCH_TITLE", (String)map.get("PJT_SCH_TITLE"));
  41. //              FileInfoMap.put("PJT_SCH_CONTENT", (String)map.get("PJT_SCH_CONTENT"));
  42.             }
  43.         }
  44.         return FileInfoMap;
  45.     }


2.Service 객체에서 쓰는방법 예시 (컨트롤러에서 서비스로 보내준뒤 서비스에서 파일유틸 호출)

Java 0.27 KB
  1.     //serviceImpl.java
  2.     @Override
  3.     public void insertProjectSchedule(Map<String, Object> map, HttpServletRequest request) throws Exception {
  4.         Map<String, Object> resultMap = fileUtils.parseInsertFileInfoOne(map, request);
  5.         projectDAO.insertProjectSchedule(resultMap);
  6.     }


3.마이바티스 예시 (많은 파일 속성을 갖고있지만, 이 프로젝트에선 저장된 파일명만 쓴다고 가정

SQL 0.32 KB
  1.         <!-- 예시 sql -->
  2.         INSERT INTO PJT_SCHEDULE
  3.         (
  4.             PJT_SCH_IDX,
  5.             PJT_IDX,
  6.             SCH_TITLE,
  7.             SCH_CONTENT,
  8.             CREA_DATE,
  9.             DEL_FLAG,
  10.             IMG_NAME
  11.         )
  12.         VALUES
  13.         (
  14.             PJT_SCHEDULE_IDX_SEQ.NEXTVAL,
  15.             #{REFER_IDX},
  16.             #{PJT_SCH_TITLE},
  17.             #{PJT_SCH_CONTENT},
  18.             SYSDATE,
  19.             'N',
  20.             #{STORED_FILE_NAME}
  21.         )



4.로컬에 저장된 사진들을 띄울때. (파일패스 경로 전역변수는 적절한곳에 추가해줌)

Java 1.01 KB
  1.  
  2.     /* 이미지 파일불러올때 */
  3.     //Commoncontroller.java
  4.     //이미지 파일 띄울때 컨트롤러를 경유해서 로컬 저장소에 접근하는 것
  5.     @RequestMapping(value="common/getImage.do")
  6.     protected void getImageFile(CommandMap commandMap, HttpServletResponse response) throws Exception
  7.     {
  8.         String fileName = (String) commandMap.get("IMG_NAME");
  9.  
  10.         if(!fileName.isEmpty())
  11.         {
  12.             File file = new File(CommonUtils.filePath+fileName);
  13.             response.setHeader("Content-Type""image/*");
  14.             response.setHeader("Content-Length"String.valueOf(file.length()));
  15.             response.setHeader("Content-Disposition""inline; filename=\"" + file.getName() + "\"");
  16.             Files.copy(file.toPath(), response.getOutputStream());
  17.            
  18.             response.getOutputStream().flush();
  19.             response.getOutputStream().close();
  20.         }
  21.     }
  22.  
  23.  
  24.     //common utils.java에 변수추가(파일패스)
  25.     public static final String filePath = "C:\\dev\\upload\\";



Scenario: 동적으로 생성된 a 태그(컨텐츠 제목) 을 클릭하면 삭제를 할 수 있게 하려고했다. on(click,function()) 키워드로 했는데 아무리 해도 안먹는 것이었다. 그래서 차선책으로 동적으로 생성시킬때 onclick='fn_delete(this)' 이런식으로 줘서 삭제를 시키려고했더니, fn_delete 함수에까지는 접근을 하는데 this obj를 죽어도 못찾는 것이었다. 좌절의 연속..





Solutions: 방법은 아주 간단했다 ㅡㅡ;; on 대신 live 키워드를 써주면 된다.


  1.     $('#schedule_area').empty();
  2.     $.ajax({
  3.         type : "POST",
  4.         url : "??????",
  5.         data : "IDX="+pjtId,
  6.         dataType: "json",
  7.         async: false,
  8.         success : function(data, textStatus, xhr) {
  9.             $.each(data, function(key, val){
  10.                 var dayForm = "<div class='media regi-item'>" +
  11.                                 "<a class='thumb-left' name='sch_title_thumb' href='#this' >" +
  12.                                     "<img src='/mobiza/common/getImage.do?IMG_NAME="+val['IMG_NAME']+"' alt='$TITLE'>" +
  13.                                 "</a>" +
  14.                                 "<div class='media-body'>" +
  15.                                     "<h4 class='rs day_detail'>" +
  16.                                         "<a class='be-fc-orange fw-b' id='sch_title' name='sch_title' href='#this' onfocus='this.blur()' '>"+val['SCH_TITLE']+"</a>" +
  17.                                     "</h4>" +
  18.                                     "<p class='rs comment-content'>"+val['SCH_CONTENT']+"</p>" +
  19.                                     "<input id='SCH_IDX' name='SCH_IDX' type='hidden'  value='"+val['PJT_SCH_IDX']+"'>" +
  20.                                 "</div>" +
  21.                                "</div>";               
  22.                
  23.                 $('#schedule_area').append(dayForm)
  24.             });
  25.         },
  26.         error : function(request,status,error) {  
  27.            alert("code:"+request.status+"\n"+"error:"+error);
  28.         }
  29.      });

쿼리 결과에따라 동적으로 태그를 생성하는 ajax


text 0.19 KB
  1.         $("a[name='sch_title'],a[name='sch_title_thumb']").live("click", function(e){ //제목 이나 섬네일 클릭시
  2.             e.preventDefault();
  3.             fn_schDelete($(this));
  4.         });

해결 script

Scenario: 이미지를 웹페이지에서 업로드 후 업로드된 폴더에서 img 태그를 써서 꺼내올려고했는데 여러가지 문제가있었다.

Solutions: 맨처음에는 배포 프로젝트 폴더에만 넣어놓으면 되는줄 알았는데, 생각보다 복잡하다 reponsegetOutputstream 을써주어야한다.



****

9
5985
#224322 Q&A java jsp

이미지 업로드 후 view 할 때 질문입니다.


이클립스+톰캣으로 작업중에 있습니다.

프로젝트가 완료된 후 서비스를 할 때는 war를 뽑아서 톰캣에 디플로이시켜서 구동할 생각입니다.

현재 게시판에시 이미지를 업로드 할 때 진행중인 프로젝트의 workspace에 파일이 업로드 되도록 하였습니다.

업로드경로는 c:/eclipseWorkspace/ProjectName/WebContent/files/
에 올라가도록 하였습니다.

현재 파일 업로드는 정상적으로 되고 있습니다.

한가지 문제가 발생했는데 이미지가 업로드된 게시물을 선택해서 볼 때
그림이 나오지 않더군요. 그런데 이클립스에서 Project Explorer에서 F5를 해주고 나서 다시 게시물을 보면 업로드된 이미지가 잘 나타납니다.

게시물을 올리거나, 수정하고나, 삭제할 때 항상 지금처럼 이클립스 Project Explorer에서 F5를 해주어야 하는건지....

아니면 뭔가 다른 방법이 있다면 알려주셨으면 합니다.
0
1
  • 답변 9

  •  **** 

    흐음... 개발서버의 톰캣에 war 디플로이해서 해봤는데 여기서는 그런거없이 바로바로 되네요.
    이클립스로 톰캣 구동해서 테스트 하는 개발pc에서는 계속 새로고침 해줘야되는건가요?
    0
  •  ****

     

    프로젝트는 WAS의 배포경로에서 실행되게 됩니다.
    이클립스 + 톰캣으로 개발시
    workspace의 소스가 바로 실행되는 것처럼 느껴지지만
    실제로는 이클립스가 소스 변경이 감지될 때마다 자동으로 톰캣 배포 경로로 소스를 복사(배포)해주고
    배포 경로에서 소스가 실행되는 겁니다.
    workspace의 경로와 톰캣에 배포된 소스의 경로는 다르고요.

    그러니까 현재 상황은
    로컬에서는 실행되는 경로(배포 경로)와 업로드 경로(workspace)가 다르기 때문에
    이미지를 찾지 못하는 것이고요,

    개발서버에 올렸을 때는 업로드 경로를 배포 경로 밑의 files로 지정하셨을테니
    이미지가 제대로 보이는 것이겠죠.
    0
  •  ****
     
    아 그리고 로컬에서 실행시
    업로드 후 이클립스의 소스를 새로고침 하면 그 때부터는 제대로 보이는 이유도
    이클립스가 새로고침 하면서 workspace/files에 저장된 이미지를 배포 경로쪽으로 복사해주기 때문에
    그때부터는 제대로 보이는 겁니다.
    0
  •  ****
     
    NG님//답변 감사합니다 ^^. 그 경로를 찾아봐야겠네요!!!
    0
  •  ****

     

    말씀하신대로 경로 찾아내서 거기에 업로드 되도록 했더니 바로바로 됩니다.
    감사합니다!!!
    0
  •  ****

     

    워크스페이스 경로/.metadata/.plugins/org.eclipse.wst.server.core

    톰캣 플러그인의 배포경로입니다.
    요 경로 밑에서 찾아보세요.

    그리고 로컬에서 테스트할 때는 배포되거나 clean할 때마다
    기존에 업로드된 이미지들은 지워질거에요.
    0
  •  ****

     

    NG님//네 말씀하신대로 수정되서 자동으로 reload??하거나 하니까 없어지네요.
    서버에서 war가 새로 디플로이될때도 없어지는데... 이건 방법이 없는 건가요?
    0
  •  ****

     

    그럴 경우
    파일 업로드/다운로드처럼 아예 별도의 디렉토리에 저장하게 한 뒤
    이미지를 response.getOutputStream()에 담아주는 서블릿을 만들어서 불러다 쓰는 방법이 있습니다.

    img 태그의 src 값도 /files/img.jpg 이런식이 아니라
    /imgLoad.do?imgName=img.jpg 처럼 서블릿을 호출하는 식으로 바꾸고요.

    이 방법이 어려우면 배포하기 전에 이미지 업로드 디렉토리를 백업해뒀다가
    배포 후 덮어씌우는 식으로 해야할 겁니다.
    0
  •  ****

     

    NG님//그렇군요. 흐으... 아는게 없다보니 힘들군요. 더 연구해봐야겠습니다.
    0


response 객체를 써서 이미지를 꺼내오는 방법. 좀더 연구를 해봐야 알 듯하다.

Java 0.70 KB
  1. @WebServlet("/files/*")
  2. public class FileServlet extends HttpServlet {
  3.  
  4.     @Override
  5.     protected void doGet(HttpServletRequest request, HttpServletResponse response)
  6.         throws ServletException, IOException
  7.     {
  8.         String filename = URLDecoder.decode(request.getPathInfo().substring(1)"UTF-8");
  9.         File file = new File("/path/to/files", filename);
  10.         response.setHeader("Content-Type", getServletContext().getMimeType(filename));
  11.         response.setHeader("Content-Length"String.valueOf(file.length()));
  12.         response.setHeader("Content-Disposition""inline; filename=\"" + file.getName() + "\"");
  13.         Files.copy(file.toPath(), response.getOutputStream());
  14.     }
  15.  
  16. }






Scenario: 게시판을 만들고나서 이제 글내용을 불러와보니 엔터등 <br> 인식이 안되는 치명적인 현상이 있엇다.

 db에서 불러올때 엔터를 <br>로 인식하게 하는 util은 쉽게 가져올수 있엇는데, 

 <%= utils.getContent(String)%> 이런식으로 써야했다. 나는 db에서 Content를 불러올때 ${dto.content} 이런식으로 불러오는데

 아무리 짱구를 굴려봐도 <%= utils.getContent( ${dto.content} )%> 이런식으로 쓸 수 가 없었다.

그래서 custom el function을 만들기로 했다 (처음엔 1시간이면 만들줄 알았지....)



solution: (아래)


1.먼저 Util파일을 만든다 (이 유틸은 디비에서 글을 불러올때 엔터를 <br>로 대입해주는 유틸이다)

주의점: 만약 bean을 사용하면 메소드를 static으로 선언해줄 필요가 없지만, 우리는 custom el funtion을 만들어야 하기때문에 무조건 static으로 만들어야한다

ContentForm.java

Java 0.56 KB
  1. public class ContentForm {
  2.     public static String getContent(String comment) {
  3.         return br(" ""<br>", comment);
  4.     }
  5.  
  6.     public static String br(String first, String brTag, String comment) {
  7.         StringBuffer buffer = new StringBuffer();
  8.         StringTokenizer st = new StringTokenizer(comment, "\n");
  9.         int count = st.countTokens();
  10.         buffer.append(first);
  11.         int i = 1;
  12.         while (st.hasMoreTokens()) {
  13.             if (== count) {
  14.                 buffer.append(st.nextToken());
  15.             } else {
  16.                 buffer.append(st.nextToken() + brTag);
  17.             }
  18.             i++;
  19.         }
  20.         return buffer.toString();
  21.     }
  22. }


2.tld 파일을 만들어준다

el-funtion.tld

XML 0.82 KB
  1. <?xml version="1.0" encoding="UTF-8" ?>
  2.  
  3. <taglib xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4.    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
  5.    version="2.0">
  6.  
  7.     <description>CommonUtil functions library</description>
  8.     <display-name>CommonUtil functions</display-name>
  9.     <tlib-version>1.1</tlib-version>
  10.     <short-name>mobizaUtil</short-name>
  11.     <uri>tld/el-funtion.tld</uri>
  12.  
  13.     <function>
  14.         <description>글내용 엔터 등, 줄바꿈 인식하게하기</description>
  15.         <name>getContent</name>
  16.         <function-class>경로.ContentForm</function-class>
  17.         <function-signature>java.lang.String getContent(java.lang.String)</function-signature>
  18.     </function>
  19. </taglib>


3. web.xml에 uri와 tld 파일 경로를 맵핑시켜준다 (생략가능)

web.xml

XML 0.25 KB
  1.     <!-- Custom UL TAG을 쓰기위함이다 -->
  2.     <jsp-config>
  3.         <taglib>
  4.             <taglib-uri>
  5.               tld/el-funtion.tld
  6.             </taglib-uri>
  7.             <taglib-location>
  8.              {프로젝트상 실제경로}/el-function.tld
  9.             </taglib-location>
  10.         </taglib>
  11.     </jsp-config>


4.jsp 페이지 내에서 사용예

만약 web.xml에서 맵핑을 하지 않는다면

<%@ taglib uri="WEB-INF/tld/el-funtion.tld" prefix="moutil" %>

이런식으로 실제 파일이 위치한 경로를 맵핑시켜주어도 무방하다.

ex.jsp

  1. HTML 0.12 KB
  1. <%@ taglib uri="tld/el-funtion.tld" prefix="moutil" %>
  2.  
  3.  
  4. <body>
  5.     내용:${moutil:getContent(dto.bContent) }
  6. <body>


Scenario: Instargram 에서 처럼 파일 업로드 메뉴이미지만 누르면 파일첨부 창이뜨고 내이미지를 첨부할려고했다


Solutions:(아래)

myprofile.jsp (html코드 생략)

jQuery 0.98 KB
  1. $(function() {
  2.     //이미지 클릭시 업로드창 실행
  3.     $('#profile_file_add').click(function() {
  4.         console.log('fileadd');
  5.         $("input[name='fileProfile']").click();
  6.        
  7.     })
  8.     //업로드 파일체인지가 됬을경우 실행되는 이벤트  form태그에 fileProfile은 hidden으로 넣어줌
  9.     $("input[name='fileProfile']").change(function(e){
  10.        
  11.         $( "#frm_profile_img" ).submit();
  12.         $("input[name='fileProfile']").val();
  13.         var frm = document.getElementById('profile_file_add');
  14.         frm.method = 'POST';
  15.         frm.enctype = 'multipart/form-data';
  16.         var fileData = new FormData(frm);
  17.      
  18.         // ajax
  19.         $.ajax({
  20.             type:'POST',
  21.             url:'/BoardProject/profileUpdate.ref',
  22.             data:fileData,
  23.             processData: false,
  24.             contentType: false,
  25.             success : function(data, textStatus, xhr) {
  26.                 console.log('success');
  27.             },
  28.             error : function(request,status,error) {  
  29.                alert("code:"+request.status+"\n"+"error:"+error);
  30.             }
  31.         });
  32.     })
  33. })



service 영역.java

Java 1.25 KB
  1. public class ProfileUploadCommand implements BoardCommand {
  2.  
  3.     @Override
  4.     public void execute(HttpServletRequest request, HttpServletResponse response) {
  5.         DAO dao = new DAO();
  6.         HttpSession session = request.getSession();
  7.         String mid=(String)session.getAttribute("mid");
  8.         String contents ="";
  9.         String fileName = "";
  10.         //String profimg = ""; //프로필 이미지
  11.         //**************** File 추가부분****************//
  12.         String pathname = "C:\\dev\\profile";
  13.         File f = new File(pathname);
  14.         if (!f.exists()) {
  15.             // 폴더가 존재하지 않으면 폴더 생성
  16.             f.mkdirs();
  17.         }
  18.         String encType = "UTF-8";
  19.         int maxFilesize = 5 * 1024 * 1024;
  20.  
  21.         // MultipartRequest(request, 저장경로[, 최대허용크기, 인코딩케릭터셋, 동일한 파일명 보호 여부])
  22.         try {
  23.             MultipartRequest mr = new MultipartRequest(request, pathname, maxFilesize, encType, new DefaultFileRenamePolicy());
  24.             fileName = mr.getFilesystemName("fileProfile");
  25.             System.out.println("upload profile mid@@@@@@"+mid);
  26.             System.out.println("upload profile filename@@@@@@"+fileName);
  27.         } catch (IOException e) {
  28.             e.printStackTrace();
  29.         }
  30.         dao.profileUpdate(mid, fileName);
  31.     }
  32.  
  33. }



-----------------------------------------------보너스---------------------------------------------------------

Scenario: db와의통신 없이 내가 첨부한 이미지를 미리보기 할 수 있는기능 추가


Solutions: 아래

파일첨부 폼.html

HTML 5 0.25 KB
  1.     <form  action="write.ref" method="post" enctype="multipart/form-data">
  2.         <input type="file" name="files">
  3.         <div id="holder"></div>
  4.         <textarea rows="5" cols="100" name="contents"></textarea><br>
  5.         <input type="submit" value="글쓰기">
  6.     </form>



  1. <script>
  2. $("input[name='profile_img']").change(function(e){
  3.     e.preventDefault();
  4.  
  5.     var file = this.files[0];
  6.         reader = new FileReader();
  7.    
  8.     reader.onload = function (event) {
  9.         var img = new Image();
  10.         img.src = event.target.result;
  11.         if (img.width > 700 || img.height > 500) { // holder width
  12.           img.width = 700;
  13.           img.height = 500;
  14.         }
  15.         $('#holder').empty();
  16.         $('#holder').append(img);
  17.     };
  18.     reader.readAsDataURL(file);
  19.    
  20.     return false;
  21. });
  22. </script>
  23.  
  24. <!-- 2. DY가 짠 첨부파일 미리보기 소스-->

  25. <script>
  26. var upload = document.getElementsByTagName('input')[0],
  27.     holder = document.getElementById('holder');
  28.  
  29. upload.onchange = function (e) {
  30.     e.preventDefault();
  31.    
  32.     var file = upload.files[0],
  33.         reader = new FileReader();
  34.    
  35.    
  36.     reader.onload = function (event) {
  37.         var img = new Image();
  38.         img.src = event.target.result;
  39.         if (img.width > 700 || img.height > 500) { // holder width
  40.           img.width = 700;
  41.           img.height = 500;
  42.         }
  43.         holder.innerHTML = '';
  44.         holder.appendChild(img);
  45.     };
  46.     reader.readAsDataURL(file);
  47.    
  48.     return false;
  49. };
  50. </script>


Scenario: 로그인을 원래 쌩 jsp로 구성을 해보았엇는데 생각보다 많은게 필요하다.

만약 Jsp로 구성한다면

로그인 -> 로그인ok(로그인이 성공했는지 확인 page) -> 성공시 main

(중간에 jsp 페이지를 안두고 구현 해볼려고했는데, 로그인 실패시 alert 창을 띄울수가 없었다.(엄청 고민해봤는데 결국 포기))

이런식으로 중간에 jsp 페이지를 하나 더 거처야한다. 그러나 Ajax로 구현하면 동적으로 할 수있다.

그리고 페이지또한

로그인->성공시 main 실패시 그냥 alert 창 하나만 띄워주면 됐다. 아주 간편하다.


Solutions:

Ajax 구문

jQuery 0.61 KB
  1. $('#btn_login').click(function() {
  2.  
  3.           var mid = $('#txt_mid').val();
  4.  
  5.           var psw = $('#txt_psw').val();
  6.  
  7.           $.ajax({
  8.  
  9.               type : "POST",
  10.  
  11.               url : "/BoardProject/login.ref",
  12.  
  13.               data : "mid=" + mid + "&psw=" + psw,
  14.  
  15.               dataType : "text",
  16.  
  17.               success : function(data, textStatus, xhr) {
  18.  
  19.                  if (data == 'loginFail') {
  20.  
  21.                       alert('로그인에 실패하였습니다.')
  22.  
  23.                   } else {
  24.  
  25.                       window.location.href = 'main.jsp';
  26.  
  27.                   }
  28.  
  29.               },
  30.  
  31.               error : function(request, status, error) {
  32.  
  33.                   alert("code:" + request.status + "\n" + "error:" + error);
  34.  
  35.               }
  36.  
  37.           })
  38.  
  39. });



controller 단 (servlet으로 프로젝트를 하던 소스코드이므로 스프링에는 다르게 적용할것)

Java 0.72 KB
  1. public class BoardLoginCommand implements BoardCommand{
  2.  
  3.     @Override
  4.     public void execute(HttpServletRequest request, HttpServletResponse response) {
  5.         String mid = request.getParameter("mid");
  6.         String password = request.getParameter("psw");
  7.         System.out.println("id"+mid+",pw"+password);
  8.         HttpSession session = request.getSession();
  9.        
  10.         DAO dao = new DAO();
  11.         ArrayList<DTO> list = dao.loginMember(mid, password);
  12.         System.out.println(list);
  13.         if(list.isEmpty()){
  14.             System.out.println("로그인안됨");
  15.             try {
  16.                 response.getWriter().write("loginFail");
  17.             } catch (IOException e) {
  18.                 e.printStackTrace();
  19.             }
  20.         }else{
  21.         request.setAttribute("list", list);
  22.         session.setAttribute("mid", mid);
  23.         }
  24.     }
  25.  
  26. }


Scenario: 사정상 Spring이 아닌 '무려' 서블릿으로 프로젝트를 진행하게 되었다. 너무 비효율적이고 힘들었지만 그래도 해내야만 했다.

그러다가 리플을 블러오는 기능을 동적으로 구현 하려고 ajax를 쓰려고 했는데, 스프링에서는 그냥 return값을 map으로 해주면 됬는데, 

이거 servlet은 return 값을 따로 넣어줄수가 없었다. (ViewPage를 넣어주면 무조건 redirect가 되버리므로 아주 복잡해졌다.)

그래서 command단에서 return으로 끝내버리고 끝내기전에 list<dto> 객체를 보내주어야 했다 (리플이 들어있는) 



Solution: 일단 list<dto>를 jason형태로 바꾸면 됐다. 이 부분은 구글의 Gson 라이브러리를 받아 해결 할 수 있었다.


view단 (ajax부분)

jQuery 0.59 KB
  1.   $.ajax({
  2.      type : "POST",
  3.      url : "/BoardProject/listreply.ref",
  4.      data : "BID="+selBid,
  5.      dataType: "json", !-- 꼭 jason으로 할 것 찍어주는 부분 -->
  6.      success : function(data, textStatus, xhr) {
  7.          $('#reply').empty();
  8.          $.each(data, function(key, val){
  9.              <!-- 로그 찍어주는 부분 -->
  10.              console.log('key:' + key + ' / ' + 'value:' + val['reply']);
  11.              $('#reply').append(val['reply']+'<br>');
  12.          });
  13.      },
  14.      error : function(request,status,error) {  
  15.         alert("code:"+request.status+"\n"+"error:"+error);
  16.      }
  17.   });



Contorller단 (command 오랜만에쓰는 부분)

Java 0.91 KB
  1. public class BoardReplyListCommand implements BoardCommand {
  2.     @Override
  3.     public void execute(HttpServletRequest request, HttpServletResponse response) {
  4.            
  5.             DAO dao = new DAO();
  6.             String bid = request.getParameter("BID");
  7.             System.out.println(bid);
  8.             ArrayList<DTO> list = dao.listreply(bid);
  9.             request.setAttribute("rlist", list);
  10.            
  11.             //타입을 json으로 바꿔줘야됨
  12.             response.setContentType("application/json");
  13.             response.setCharacterEncoding("UTF-8");
  14.            
  15.             //DTO 타입의 어레이리스트를 json 형태로 바꿔주는 구문(라이브러리 필수, zip->jar 확장자명 꼭 확인)
  16.             String gson = new Gson().toJson(list);
  17.            
  18.             try {
  19.                 //ajax로 리턴해주는 부분
  20.                 response.getWriter().write(gson);
  21.             } catch (JsonIOException e) {
  22.                 e.printStackTrace();
  23.             } catch (IOException e) {
  24.                 e.printStackTrace();
  25.             }
  26.            
  27.     }
  28. }


Scenario: 메인에 Index.jsp 에서 2개의 프레임이 있다. 왼쪽은 로그인을 할 수 있는 iframe 이고 오른쪽은 로그인 결과를 나타내주는 iframe이다.

왼쪽창에서 로그인 검증값으로 넘어간뒤 로그인이 완료되면 right의 iframe 을 동적으로  반응 시켜주어야 했다. 

즉 왼쪽 iframe 스크립트 코드에서 오른쪽으로 접근하는 부분에서 애를 많이 먹었는데 성공 할 수있었다.


Solutions:

(구문해석)

$('#right', window.parent.document).contents().find('.login').show();    //가장 애먹은 부분이었다.

위와 왼쪽 프레임에서 오른쪽 프레임으로 접근하였다.

 $('#right', window.parent.document) 이것은 부모프레임에서 #right라는 id를 가진 프레임에 접근하는 것이다.

contents().find('.login').show(); 그뒤에 그 프레임내 컨텐츠에서 login class를 가진 요소에 접근해 show() 시켜준다.


<index.jsp>

HTML 5 0.54 KB
  1. <%@ page language="java" contentType="text/html; charset=UTF-8"
  2.    pageEncoding="UTF-8"%>
  3. <html>
  4. <head>
  5. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  6. <title>Insert title here</title>
  7.  
  8. </head>
  9. <body>
  10. <h1 id=hi>여기표시 접근가능?</h1>
  11. <table width="100%" border="1">
  12. <tr><td id='frame'>
  13. <iframe src="left.jsp" width="450" height="560"></iframe>
  14. <iframe id="right" src="right.jsp" width="450" height="560"></iframe>
  15. </td>
  16. </tr>
  17. </table>
  18.  
  19. <script type="text/javascript">
  20.    
  21. </script>
  22. </body>
  23. </html>



<left.jsp>

HTML 5 1.65 KB
  1. <%@page import="java.util.Date"%>
  2. <%@page import="java.text.SimpleDateFormat"%>
  3. <%@ page language="java" contentType="text/html; charset=UTF-8"
  4.    pageEncoding="UTF-8"%>
  5. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
  6. <html>
  7. <head>
  8. <%
  9.     Date time = new Date();
  10.     SimpleDateFormat formatter = new SimpleDateFormat("yyy-MM-dd HH:mm:ss");
  11.  
  12. %>
  13. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  14. <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
  15.  
  16.  
  17. </head>
  18. <body>
  19.     <h1>left.jsp</h1><br>
  20.     <form action="LoginResult.jsp">
  21.         <% if(session.getAttribute("id") == null){%>
  22.        
  23.             아이디:  <input type="text" name="id"><br>
  24.             비밀번호: <input type="text" name="pw"><br>
  25.             <input type="submit" value="로그인">
  26.        
  27.         <script type="text/javascript">
  28.             $(function(){
  29.                 $('#right', window.parent.document).contents().find('.login').hide();
  30.                 $('#right', window.parent.document).contents().find('.logout').show();
  31.             })
  32.         </script>
  33.        
  34.        
  35.         <%} else { %>
  36.         <h3>jsp 님 로그인중</h3><br>
  37.         <input type="submit" value="LogOut">
  38.             <hr style="border:solid 2px red;">
  39.             접속시간:<% time.setTime(session.getCreationTime()); %><%=formatter.format(time) %><br>
  40.             최근 접근 시간시간:<% time.setTime(session.getLastAccessedTime()); %>
  41.             <%=formatter.format(time) %>
  42.                 <script type="text/javascript">
  43.             $(function(){
  44.                 $('#right', window.parent.document).contents().find('.login').show(); //제일 애먹었던부분
  45.                 $('#right', window.parent.document).contents().find('.logout').hide();
  46.             })
  47.         </script>  
  48.         <%}%>
  49.     </form>
  50. </body>
  51. </html>


<right.jsp>

HTML 5 0.59 KB
  1. <%@ page language="java" contentType="text/html; charset=UTF-8"
  2.    pageEncoding="UTF-8"%>
  3. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
  4. <html>
  5. <head>
  6. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  7. <title>Insert title here</title>
  8. </head>
  9. <body>
  10.     <h1>RIGHT.jsp</h1><br>
  11.     <h2 class='logout'>로그인되어 있지 않은 상태입니다.</h2>
  12.     <img class='login' alt="main" src="main.jpg" width="70%" height="70%" style="display:none">
  13.     <h2 class='login' style="display:none">접속중입니다.</h2>
  14. </body>
  15. </html>


<LoginResult.jsp> ->로그인 검증

HTML 5 1.02 KB
  1. <%@ page language="java" contentType="text/html; charset=UTF-8"
  2.    pageEncoding="UTF-8"%>  
  3.  
  4.  
  5. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
  6. <html>
  7. <head>
  8. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  9. <title>Insert title here</title>
  10. </head>
  11. <body>
  12.  
  13. <%
  14.     if("jsp".equals(request.getParameter("id")) && "1234".equals(request.getParameter("pw")))
  15.     {
  16.         session.getId();
  17.         session.setAttribute("id""jsp");
  18.         session.setAttribute("pw""1234");
  19. %>
  20.     <script type="text/javascript">
  21.         alert("로그인에 성공하였습니다.")
  22.         location.href="left.jsp";
  23.     </script>
  24. <%  } else {
  25.         if(session.getAttribute("id") != null)
  26.         {
  27.             session.invalidate();
  28. %>
  29.         <script type="text/javascript">
  30.             alert("로그아웃 되었습니다.");
  31.             location.href="left.jsp";
  32.         </script>
  33. <%      } %>
  34.         <script type="text/javascript">
  35.             alert("비밀번호가 틀렸습니다.");
  36.             location.href="left.jsp";
  37.         </script>
  38. <%} %>
  39.  
  40. </body>
  41. </html>






<접속 안되있을시>

<로그인 성공시>


좋은 커밋 메시지 작성하기



좋은 커밋 메시지는 다음 세가지 중요한 목적이 있다:

  • 리뷰 프로세스를 빠르게 만든다.
  • 좋은 릴리즈 노트를 작성하게 돕는다.
  • 미래의 코드 메인테이너를 돕는다. (그 메인테이너가 당신일 수도 있다!) 어떤 코드 변경이 일어났는지, 왜 이 특정한 기능이 추가되었는지 수년 후에 이야기 해도 알 수 있도록 말이다.

커밋 메시지의 구조는 다음과 같다. 이 내용은 git scm에서 자세하게 확인할 수 있다:

(50자 미만의) 변경에 대한 짧은 요약
필요하다면 상세한 설명을 첨가한다. 행 당 72자가 넘지 않도록 유의한다. 맥락에 따라 첫 줄은 이메일의 제목처럼, 나머지는 본문처럼 다뤄지는 경우가 있다. 요약과 본문을 구분하는 빈 행은 본문을 생략하지 않는 이상 필수적이다. rebase와 같은 도구를 사용하면 혼란을 줄 수 있기 때문이다.
추가적인 문단은 빈 행 다음에 작성한다.
- 개조식 서술 또한 괜찮음 - 블릿(bullet)으로 하이픈(-)이나 별표(*)를 사용하고, 한 칸의 공간을 띄고 각 항목 사이 빈 행을 넣는 방식이 일반적이나 다양한 관례가 있음.

해야 할 일

  • 요약과 설명을 작성할 때는 자신이 무엇을 했는지를 명령형으로 작성한다. 즉 다른 사람에게 이 일을 지시하듯 작성한다. “고쳤다 fixed”, “추가했다 added”, “변경했다 changed” 대신 “고치다 fix”, “추가하다 add”, “변경하다 change” 식으로 작성한다.
  • 항상 두번째 빈 행을 추가한다.
  • 커밋 메시지에서 줄 바꿈을 한다. (gitk등 커맨드 환경에서 커밋 메시지를 수평 스크롤 없이 쉽게 읽을 수 있는데 도움이 된다.)

하지 말아야 할 일

  • 요약 끝에 마침표를 찍지 않는다. 요약은 제목이다. 제목은 마침표로 끝나지 않는다.

  • 커밋을 요약하는데 어려움을 느낀다면 커밋 안에 여러 로직의 변화 또는 버그 수정이 있기 때문일 것이다. 이럴땐 git add -p 명령어를 사용해 여러 커밋으로 분리하는 것이 낫다.

레퍼런스

커밋 메시지에 대한 좋은 논의를 다음 블로그 포스트에서 확인할 수 있다.


+커밋 메시지에 대해


지난 몇 주 동안 놀랄 만큼 많은, 커밋 메시지에 대한 토론을 읽게 되었다. 그 중에는 개발자와 함께 막 새 프로젝트를 시작하려는 사람들도 많았다. 그래서 그들을 돕기 위해 커밋을 할 때 해야 할 일과 그 일을 왜 해야 하는지에 대한 목록을 작성해봤다. (힌트: 리눅스 커널 메일링 리스트는 이 일을 아주 바람직하게 하고 있다. 그곳에서 배우자.)

모든 소프트웨어 프로젝트는 협동 프로젝트다. 적어도 프로젝트엔 두 명의 개발자가 일하게 되어 있다. 나 혼자 최초 개발자로 스스로 개발을 한다고 해도 몇 주, 몇 달이 지난 후에 작성한 코드가 무엇인지 생각해내야 하는 미래의 내가 존재한다. 미래의 나는 새로운 버그가 발생하거나 새로운 기능을 추가해야 할 때마다 매번 특정 부분의 코드에 대한 맥락을 다시 파악해야만 한다.1

코드 조각의 맥락을 다시 파악하는 일은 정말 낭비일 수밖에 없다. 물론 이 일을 완전히 회피할 수는 없겠지만, 이 시간을 최소화할 수 있도록 노력해야 한다. 바로 커밋 메시지가 그 역할을 담당한다. 그렇기 때문에 커밋 메시지를 보면 그 개발자가 좋은 협력자인지 아닌지를 알 수 있게 된다.

좋은 커밋 메시지는 패치에 관한 다음 세 가지 질문에 답을 할 수 있어야 한다:

  • 왜 이 코드가 필요한가? 코드는 버그 수정, 기능 추가, 성능 향상, 신뢰성, 안정성을 위한 변경일 수 있다. 물론 단순한 오·탈자 교정일 수도 있다.
  • 어떻게 이슈를 해결했는가? 짧아서 명백한 패치의 경우 이 부분을 생략해도 된다. 다만 깊은 수준의 묘사로 어떻게 문제에 접근했는지 나타내야 한다.
  • 패치가 어떤 영향을 만드는가? 명백한 부분을 포함해 벤치마크 결과, 부작용 등을 포함할 수 있다.

이 세 가지 질문으로 실제 코드 변경에 대한 맥락을 알 수 있게 된다. 또한 리뷰어와 다른 개발자가 그 맥락을 통해 차이점을 보고 적절한 방법을 선택해 문제에 접근했는지 확인하게 된다. 또한 좋은 커밋 메시지는 메인테이너에게 안정 브랜치에 포함해도 괜찮은지, 또는 배포에 포함해도 괜찮은지 결정하는데 도움된다.

이 세가지 질문에 대한 답이 없는 패치는 대부분 쓸모 없는 패치다. 이런 경우에는 이 패치가 어떤 일을 하고 어떻게 이슈를 해결했는지 직접 찾아야 하기 떄문에 리뷰어에게 부담만 될 뿐이다. 복잡할대로 복잡한 패치에 많은 수의 리뷰어가 필요한 상황. 그 의미는 결국 원 개발자가 좋은 커밋 메시지를 작성하지 않았다는 이유만으로 많은 인력 투입 시간(man-hours)이 낭비된다는 뜻이다. 거기에 만약 메인테이너가 프로젝트의 소스 컨트롤 관리 통제를 철저히 하고 있다면, 개발자가 제출한 패치는 거절 당할 것이고 개발자는 다시 시간을 소비해 패치를 다시 작성해야 하며, 리뷰어는 리뷰에 또 시간을 소비하게 되는, 최악의 경우도 발생할 수 있다. 이처럼 시간 낭비는 빠르게 늘어난다. 단지 몇 분 시간을 투자해서 커밋 메시지를 작성하는 것이 이런 비경제적인 시간 소비를 없앨 수도, 최악의 상황을 만들 수도 있는 것이다.

오픈소스가 아닌 일반 소프트웨어 회사도 이 내용을 충분히 고려해야 한다. 적당한 소스 컨트롤 관리 규칙이 없으면 결국 비용이 발생한다.

어떻게 해야 더 잘할 수 있을까

물론 이상적인 커밋 메시지는 이래야 한다는 엄격한 정의는 없다. 하지만 몇 가지 일반적인 규칙은 있다. 커밋은 정확히 하나의 로직 변경을 포함해야 한다. 로직 변경은 새로운 기능을 추가하거나 특정 버그를 수정하는 것 등을 의미한다. 몇개의 단어로 고수준의 변화를 묘사할 수 없다면 단일 커밋으로는 너무 복잡한 상태인 것이다. 변경은 가능한 한 그 스스로 이해할 수 있도록 간결해야 한다. 많은 패치에서 발생한 에러가 작은 패치에서 발생한 에러보다 낫다. 가장 우선이 되는 규칙으로, 커밋 메시지만 읽어도 다른 개발자가 납득할 만큼 비슷한 시간을 들여 같은 패치를 구현할 수 있어야 한다.

git을 사용한다면 git add -p (또는 -i)를 활용해 각각의 변경 사항에 따라 로직을 이해할 수 있는 수준의 단일 커밋 단위로 쪼개야 한다.

Git 커밋 양식

만약 패치를 git으로 제출한다면, 그 양식은 거의 표준화 되어 있다. 첫 행은 변경에 대한 요약이다. (행의 최대 길이는 프로젝트마다 다르지만 일반적으로 행 당 50자에서 78자 사이다.) 이 첫 줄을 가장 많이 보게 되고 그만큼 중요하다. 많은 git 도구가 이 방식으로 동작하거나 이 양식에 최적화되어 있다. 첫 행 요약 다음으로 빈 행을 입력하고 그 뒤로 필요에 따라 패치에 대한 상세 내역을 여러 문단으로 작성한다. 코드를 설명하지 말고 의도와 접근 방식을 설명한다. 로그는 현재형으로 작성한다.

로그를 사랑하는 방법을 배울 것

나는 과거에 CVS를 사용했는데 (SVN도 조금) 이 도구는 정말 사용하기가 쉽지 않았다. 거의 쓸모가 없었는데 도구도 그랬고 사용 가능한 정보도 그랬다. 현재는 코드를 들여다 보는 것 보다 git의 로그를 더 자주 본다. git 로그 도구는 일하고 있는 프로젝트에서 CVS의 로그나 커밋 규칙에 비해 대단히 뛰어나기 때문에 훨씬 편리하다. 코드보다 git 로그를 더 많이 붙잡고 왜 이 코드가 이런 방법으로 작성되었는지 git을 통해 살펴보는데 대부분의 시간을 쓴다. 이 방법은 확실히 많은 시간과 노력을 아끼게 해준다. X 서버 버그에서 가장 짜증나는 점은 코드가 XFree86에서 넘어오는 과정에 git 히스토리가 남아있지 않은 곳에서 나타난다는 점이다. 만약 아직 소스 컨트롤 관리의 로그 도구를 사용하고 있지 않다면 이 도구와 더 친해지길 추천한다.

하면 안되는 것

커밋을 할 때 평균적으로 나타나는 몇 가지 일반적인 죄악이 있다. (그렇다. 읔.)

  • 소스 컨트롤 관리는 백업 시스템이 아니다! 개인적으로 정말 싫어하는 유형이다. 개발자 중에는 이 도구를 퇴근용 커밋 즉, 퇴근하기 직전에 변경한 모든 코드를 커밋하는 사람이 있다. 그 결과는 아무짝에 쓸모가 없다. 코드 이곳 저곳에서 확인되는 변경점은 몇개월이 지나면 그 누구도 이해할 수가 없다. 실제 코드 작성자를 포함해서 말이다. (덧붙여: 대학교는 절대 이런 쓰레기 방식으로 가르치지 말 것.)
  • 파일 당 커밋. 파일 하나 이상에서 로직 변경이 실제로 발생하지 않았는데도 커밋하는 경우가 있는데 지나치게 분리해서 커밋하면 안된다.
  • 게으른 커밋 메시지, “여러가지 고치고 정리했음” 또는 이와 비슷하게 작성한 모든 커밋을 의미한다. 이런 경우를 비FOSS 프로젝트에서 종종 본 적이 있었는데 이런 커밋은 결국 당신에게 되돌아와 상처를 입힌다. 알려진 버그인지 알아내는 것은 불가능에 가깝고 문제를 분리하기도 어려울 뿐더러 그 누구도 프로젝트에서 무슨 일이 일어나고 있는지 따라가기 어렵게 만든다.
  • 하나의 패치 안에 두 가지 변경. 예를 들면 “버그 2345를 수정했고 모든 foo를 bar로 수정했음”과 같은 커밋이다. 버그 2345에서 명칭 변경이 요구되고 있다 하더라도 이런 커밋은 여러개의 패치로 분리해야 한다. 이 버그 픽스를 안정 브랜치에 적용해야 하는 상황일 때 다른 내용이 함께 있기 때문에 적용할 수가 없다. 잘못된 패치를 유용한 덩어리에 넣는 일은 누군가 직접 처리하기 전까진 프로젝트에 아무런 가치를 더해주지 못하는 일이라 가장 시간 소모적이고 짜증나는 일 중 하나다.
  • 코드 변경에 공백 변경을 함께 넣는 경우. 사막에서 바늘 찾는건 재미있는 게임이지만 패치를 들여다보고 있을 때는 전혀 아니다. 이 방법은 분명 버그를 소개하는 가장 강력한 방법이긴 하다. 비록 수백 줄의 코드에 들여쓰기를 변경해 모든 코드가 변경된 상태로 표시되고 있으니 말이다. 재미로 한건지 장점이 있어서 한 것인지는 둘째치고 어느 위치에 버그가 있었고 그 버그를 어떻게 수정했는지 거의 아무도 찾을 수 없다.
  • 너무나도 사랑스러운 코드 누락. 새로운 기능을 추가하기 위해 수백 줄 코드 패치를 작성하는 동시에 이 기능을 위해 기존에 있던 인프라 구조를 절반 이상 재작성을 한 경우다. 그 결과로 수백 줄의 코드를 리뷰해야 하고 매번 이 영역에 있던 코드와 관련된 버그를 발견하게 된다.인프라 구조를 한번에 한 조각씩 수정하는 것으로 시작하고 그 작업을 하고서 맨 위에 새로운 기능을 꼽았으면 훨씬 쉽고 적은 시간을 사용했을 것이다. 위와 같은 방법을 자주 사용해서, 즉 빈번하게 코드 덤프를 통채로 적용하는 프로젝트가 있다면 이는 외부 개발자들의 사기를 떨어뜨린다. 당신이라면 코드를 기여하는 프로젝트가 아니라 지독한 잡음에서 신호를 골라내는 일에 시간을 쓰는 프로젝트에 참여하겠는가?
  • 패치와 관계 없는 공백 변경. 리뷰어는 패치에 관한 큰 그림을 생각해야 한다. 공백만 있는 덩어리는 혼란스럽다. 리뷰어는 그 공백이 실제 변경인지 무시해도 되는지 확인하기 위해 더 추가적인 노력을 기울이게 된다. 빈 행이 추가되거나 제거되는 경우는 그렇게 나쁘지 않다. 정말 나쁜 경우는 들여쓰기 변경이다.

위와 같은 상황에는 수많은 변명도 존재하는데 대부분 다음과 같이 대답하기를 좋아한다. “그래도 동작하잖아요!” 물론 동작은 한다. 하지만 코드는 정적이지 않다. 얼마 간의 시간 내에 코드는 아마 이동하고, 다시 작성될 것이며, 다른 방법으로 호출되거나 버그가 포함되어 있다는 사실이 발견될 수도 있다. 동시에 최초의 개발자는 코드를 움직이고 누구도 왜 그 코드가 그렇게 움직였는지 모를 수도 있다. 최악의 경우는 모든 사람이 코드를 만지는 것을 두려워하는 상황인데 누구도 어떻게 실제로 동작하는지 알 수 없기 때문이다.

또 다른 일반적인 변명은 다음과 같다. “하지만 나는 이 프로젝트에 참여하는 유일한 사람입니다.” 사실이 아니다. 모든 소프트웨어 프로젝트는 (위에서 보는 것과 같이) 협동 프로젝트다. 누구도 단순히 근시안적으로 생각하지 않는다. 특히 FOSS 프로젝트는 외부의 기여자 즉, 테스터, 개발자, 우선순위를 정하는 사람, 사용자 등에 의존적이다. 그들이 참여하게 만드는 게 어렵다면 그 프로젝트는 더 쉽게 망할 것이다.

좀 덜 일반적이지만 최근에 자주 보이는 또 다른 변명은 소스 컨트롤 관리가 너무 느리다는 불평이다. 분산 소스 컨트롤 관리는 이 이슈를 해결했기 때문에 시간도 절약하고 돈 절약에도 아마 보탬이 될 것이다.


번역: By Haruair 

http://www.haruair.com/blog/2738

원문

https://github.com/erlang/otp/wiki/Writing-good-commit-messages




알아야할 사전 지식.

1.Json 이란 무엇인가?

 JavaScript Object Notation의 약자로써, 자바스크립트를 위한 것이며, 객체 형식으로 자료를 표현하는 것이다. 그냥 [제이:슨] 이라고 읽으면 된다.

2.이것은 새로운 언어인가?

 아니다 단순히 데이터 포맷일 뿐이다. XML,INI 파일 등과 같은 비슷한 것이라고 생각하면 된다.




Scenario: Ajax를 이용해서 여러 데이터 파일을 파싱하고자함. 그중 첫번째는 json 파일


Solutions : (아래)


<Ajax_getjSON.html>

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>JSON 이용하기</title>
  6. <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
  7. <script type="text/javascript">
  8.     $(function() {
  9. //      $.ajax({
  10. //          url: "items.json",
  11. //          contentType: "application/x-www-form-urlencoded; charset=UTF-8",
  12. //              dataType: "json",    
  13. //          success: function(result) {
  14. //              $.each(result, function(i, val) {
  15. //                  console.log(val['id']+':'+val['name']);
  16. //              })
  17. //          },
  18. //          error: function(jqXHR, textStatus, errorThrown) {
  19. //              alert("Error: " + textStatus + " errorThrown: " + errorThrown);
  20. //          }
  21. //      });
  22. //ajax 이용
  23.  
  24.         $.getJSON("items.json", function(result) {
  25.             $('#treeData').append("<tr><td>price</td>"+"<td>name</td>"+"<td>price</td>"+"<td>description</td></tr>");
  26.             $.each(result, function(i, val) { //result는 each 문에 val과 매칭시켜주기위함이고 , 인자 i와 val은 each함수의 시그니쳐다.
  27.                 console.log(typeof(i));
  28.                 console.log(val['id']+val['name']+val['price']+val['description']);
  29.                 $('#treeData').append("<tr><td>"+val['id']+"</td>"+"<td>"+val['name']+"</td>"+"<td>"+val['price']+"</td>"+"<td>"+val['description']+"</td></tr>");
  30.             });
  31.         });
  32. });  
  33. </script>
  34. <style>
  35. td, th {
  36.     border: 1px solid gray;
  37. }
  38. </style>
  39.  
  40. </head>
  41. <body>
  42.     <table id="treeData">
  43.     </table>
  44. </body>
  45. </html>


<items.json>

  1. [
  2.   {
  3.     "id": "1",
  4.     "name": "레몬",
  5.     "price": " 3000",
  6.     "description": "레몬에 포함되어 있는 쿠엔산은 피로회복에 좋다. 비타민C도 풍부하다."
  7.   },
  8.   {
  9.     "id": "2",
  10.     "name": "키위",
  11.     "price": " 2000",
  12.     "description": "비타민C가 매우 풍부하다. 다이에트와 미용에도 매우 좋다."
  13.   },
  14.   {
  15.     "id": "3",
  16.     "name": "블루베리",
  17.     "price": " 5000",
  18.     "description": "블루베리에 포함된 anthocyanin(안토시아닌)은 눈피로에 효과가 있다."
  19.   },
  20.   {
  21.     "id": "4",
  22.     "name": "체리",
  23.     "price": " 5000",
  24.     "description": "체리는 맛이 단 성분이 많고 피로회복에 잘 듣는다."
  25.   },
  26.   {
  27.     "id": "5",
  28.     "name": "메론",
  29.     "price": " 5000",
  30.     "description": "메론에는 비타민A와 칼륨이 많이 포함되어 있다."
  31.   },
  32.   {
  33.     "id": "6",
  34.     "name": "수박",
  35.     "price": "15000",
  36.     "description": "수분이 풍부한 과일이다."
  37.   }
  38. ]


어려웠던점 

1. 일단 Json 데이터의 {}가 한개일때는 파싱하기가 쉬운데, 중괄호가 여러개가 되니 하나의 오브젝트가 되어 파싱하기가 힘들어졌다. 

찾아보니  {}가 여러개인 형태를 json array라고 부른다고 하더라 (Json 데이터가 배열형태를 이룸), json array라는 키워드를 알고 구글링하니 찾기 쉬워짐

2.json 파일을 불러오다보니 한글파일이 자꾸 깨짐, 일단 결론부터 말하자면 .getJSON 으로는 utf-8 을 지정할 수 없음. .aJax를 쓰면 contentType key로 

utf-8 지정가능




+Tip 시그니쳐란 무엇인가?

나는 원래 C,C++ 부터 프로그래밍을 시작해서, 델리게이터를 만들때 시그너쳐(signature) 라는 용어를 많이썻었는데,

시그너쳐란 말그대로 어떤 함수의 선언문에  명시된 매개변수 리스트와 반환타입이다.

쉽게말해 시그니처는 함수의 형태이다.


[Signature Ex]

int getMoney(Person kim); 

다음 함수 에서 시그니쳐는 int(Person) 인것이다.


+ Recent posts