(css) table 테이블 깨짐 방지 td 줄바꿈방지

 

안녕하세요. 오늘은 테이블깨짐의 주 원인인 줄바꿈에 대해 알아보는 시간을 가져보겠습니다.

흔히 우리 테이블에서 td 태그 안에 주는 줄바꿈방지 스타일로는

 

white-space:nowrap;

 

이걸 많이 씁니다. 그런데 이것 만으로 해결되지 않는 경우가 있으니..

이와 같은 경우는 테이블이 내가 정해둔 td width 값을 유지하지 않고 막 크기를 늘리기도 하고 줄이기도 하고.. 

개발자들의 수명을 단축시킬때가 많지요.. OTL..

 

복잡한 테이블이고 특정 액션에 따라 tr 또는 td를 유동적으로 바뀌는 상황에서는 테이블이 쉽게 깨지는 경우가 많습니다. 이렇게 말하면 프론트엔드만 개발했던 분들은 도대체 그런 경우가 어딨냐고 물으시겠지만..

 

예를 들면 이렇습니다.

디자인상 행이 5개, 열이 4개인 테이블이었지만..

특정 조건(통상 if문)에 따라 tr 또는 td를

추가로 더 늘리기도 하고 줄이기도 하고...

어쩔 때는 그 안에 데이터(글자수)가 적었다가 많았다가..

그러다보면 원래 디자인 되어있던 테이블이 막 그 안의 데이터에 따라 깨지는 현상이 발생하는것이죠. 

거기에 호환성보기모드/비호환성보기모드 에 따라 상황이 왔다 갔다.. ie, 크롬에 따라 또 다르고..

이런거 잘못 걸리면 개발자 수명 단축됩니다. 상당히 짜증나요 ㅠㅠ

 

그런데 나의 이런 고민을 해결해준 단 한 줄의 스타일이 있었으니

그거슨 바로바로!!!!

 

table-layout:fixed;

 

와. 저 한 줄만 갖다 넣으면 되는거였어.. ㅠㅠ테이블 깨짐과 이상한 곳에서 줄바꿈이 되는 현상을 해소하기 위해 몇 시간을 고심하던 끝에 찾았다.

 

생각보다 별거 아니었지만.. 꽤 요긴하게 쓰일것 같은 css ㅋㅋㅋ

 

 

 

댓글과 구독은 저를 춤추게 합니당.

모두모두 즐코딩 하세열~

 

 

 

자바스크립트 브라우저 경고창 안뜨게 닫는 방법

 

javascript에서 자기자신(브라우저 창)을

닫고 싶을 때 하는 방법은

  1. window.close();

  2. self.close();

이렇게 두가지가 있다.

그런데 창을 닫을때 저렇게 쓰면 창을 닫겠냐는 경고창이 꼭 나온다.

 

예, 아니오 버튼이 한번 더 나와서 마우스로 굳이 또 클릭을 하게 만든다.

 

물론, 이 기능이 반드시 필요한 경우도 있다. 그러나 대부분의 경우 닫기 기능을 경고창 없이 진행하게 하고 싶을 것이다. 

자, 그러면 이럴 때 어떻게 하면 좋을까??

 

window.open("about:blank", "_self").close();

 

이렇게만 써주면 된다. ㅎㅎㅎ

어라, 닫아야 되는데 open이 써 있어서 이상한가??

약간의 꼼수이긴 하나, 자기 자신의 창에 자식창을 열어서 모두 닫아버리는 방법이라 보면 되겠다.

 

반대로, 자식 창에서 부모창을 이렇게 닫으려면 어떻게 하지??

 

opener.open("about:blank","_self").close();

 

이렇게 하면 된다.

아... 너무 쉽다.

 

댓글과 구독이 간절하다.. 

 

개발자들끼리는 꼭 해주세요.. ^--------^

 

 

 

 

 

 

Windows 긴 파일명 삭제하는 방법을 소개하겠습니다.
 
"원본파일 이름이 파일 시스템에서 지원하는 길이보다 깁니다"
라는 팝업을 보셨나요???
파일의 확장명이 너무 길다 뭐 그런 말이 보이면서
파일 삭제가 되지 않을때 쓰는 방법입니다.
 
이걸로 저도 구글 검색을 참 많이 했더랬죠.. 
아래의 방법이 가장 깔끔해서 공유해보려고 합니다.
엄청 쉬워요. 잘 따라해보세요.
 
1. 빈 폴더를 하나 생성한다. 예를 들면
  1) 삭제할 파일이 들어있는 폴더(sts-bundle)가 C드라이브에 있고
  2) 빈 폴더(delTemp)를 하나 더 생성하자  C:\delTemp 
 
2. 윈도 cmd 창을 열어서 (돋보기 아이콘 클릭 후 cmd를 침)
3. 해당 디렉토리가 있는 곳으로 갑니다.
  명령어 -->     cd C:\    
4. 해당 디렉토리에서 아래와 같이 친다.
  명령어 -->    robocopy C:\delTemp C:\sts-bundle /MIR   
 

이렇게 하고 나면 두 개의 폴더가 다 빈 폴더가 되어 삭제가 가능해집니다.
 
참 쉽죠? ㅎㅎ 
 
파일이 삭제되지 않아 당황하신 분들께 도움이 되었으면 좋겠네요.
 

[JAVA] unchecked or unsafe operations

 

Note : 자바파일이름.java uses unchecked or unsafe operations.

Note : Recompile with -Xlint: unchecked for details.

 

컴파일 에러.

JDK1.5이상에서는 다양한 타입의 객체들을 다루는 메소드나 컬렉션 클래스에 컴파일 시의 타입 체크를 해주는 제네릭(Generic) 메커니즘이 추가되었다. 따라서 제네릭 클래스나 인터페이스를 선언하거나 생성할 때 자료형을 명시해 주지 않으면 나는 에러이다.

단, 컴파일 시 이러한 에러가 나더라도 실행하면 정상적으로 작동이 되므로 크게 신경쓰지 않아도 된다. 

 

더 자세한 에러는 아래를 참고하여 확인하면 된다.

 

javac -Xlint 자바파일이름.java

[JAVA] MultipartParser 파일 저장경로를 파라메터로 받아서 업로드하기

 

저장 경로를 미리 정해두지 않고 파라메터로 받아서 처리하고 싶을때 

MultipartParser를 사용한다.

 

1. 클라이언트 (html)

 - ajax에서 multipart/form-data 로 보낼 것이므로 form에는 굳이 써주지 않아도 된다.

<form method="post" action="">
    <input type="hiddn" id="savePath">
    <input type="file" id="fileUpload">
    <input type="submit" value="Upload">
</form>

2. 클라이언트 (ajax)

  - 주의할 점은 파일의 경로를 먼저 append해주고 그 후에 파일을 append해준다.

//파일업로드 리스트 생성
var fileData = new FormData();
var files = $("#fileUpload").get(0).files;
 
fileData.append("savePath", "EMPLOYEE");
 
if (files.length > 0) {
    for (var i = 0; i < files.length; i++) {
        fileData.append(files[i].name, files[i]);
    }
}
 
$.ajax({
        type: "POST",
        url: hostUrl + "api/File/Upload",
        contentType: false,
        processData: false,
        async: false,
        crossDomain: true,
        data: fileData,
        success: function (data) {
            result = JSON.parse(data);
        },
        error: function (request, status, error) {
            alert("code:" + request.status + "\n" + "error:" + error);
        }
});

 

3. 백엔드(java)

- 파일데이터를 while문을 돌면서 순차적으로 파라메터로 받는다. 앞서 파일객체를 생성할 때 경로를 먼저 넣으라고 한 이유가 여기에 있다. 순차적으로 돌기 때문에 경로를 먼저 받아야 그 경로로 파일을 보낼 수 있는 것이다.

package com.file;
 
import java.io.*;
 
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import com.oreilly.servlet.multipart.*;
 
public class Upload extends HttpServlet {
 
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        process(req, resp);
        
    }
    //처리내용
    protected void process(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        PrintWriter out = resp.getWriter();
        
        // 30Mbyte 제한
        int maxSize  = 1024*1024*30;   
        String fsl = File.separator;
        String root = req.getSession().getServletContext().getRealPath(fsl);
        String rootPath = root + fsl + "Uploads";
        String savePath = "";
        
        MultipartParser mp = new MultipartParser(req, maxSize);
        mp.setEncoding("UTF-8");
 
        Part part;
        while ((part = mp.readNextPart()) != null) {
            String name = part.getName();
 
            //파일이 아닐때
            if (part.isParam()) {
                ParamPart paramPart = (ParamPart) part;
                String value = paramPart.getStringValue();
                //System.out.println("param; name=" + name + ", value=" + value);
                if (name.equals("savePath")) 
                    savePath = value;
                
            }
 
            // 파일일때
            else if (part.isFile()) {
                FilePart filePart = (FilePart) part;
                filePart.setRenamePolicy(new DefaultFileRenamePolicy()); //중복파일

                String fileName = filePart.getFileName();
                if (fileName != null) {
                    File dir = new File(rootPath + fsl + savePath);
                    if (!dir.isDirectory()){     //디렉토리인지 체크 후 없으면 생성
                        dir.mkdir();
                    }
                    //System.out.println(dir);
                    long size = filePart.writeTo(dir);
                }
                else {
                    //form type 이 file 이지만 비어있는 파라메터
                    System.out.println("file; name=" + name + "; EMPTY");
                }
            }
        }    
    }
    
}

개인적으로는 MultipartRequest로 보내는 것보다 이 방법이 더 좋은것 같다.

 

 

< MultipartRequest로 파일 업로드 하기 바로 가기 >

https://kwonyang.tistory.com/8

 

[JAVA] MultipartRequest File Upload

[JAVA] MultipartRequest File Upload 파일업로드를 하기 위해서는 oreilly.jar 를 먼저 다운받는다. 그리고 CLASSPATH 환경변수에 넣어둔다 (예: %CATALINA_HOME%\lib\oreilly.jar) 1. 클라이언트 단(Ajax) 제..

kwonyang.tistory.com

 

[JAVA] MultipartRequest File Upload

 

파일업로드를 하기 위해서는  oreilly.jar 를 먼저 다운받는다.

그리고 CLASSPATH 환경변수에 넣어둔다 (예: %CATALINA_HOME%\lib\oreilly.jar)

 

1. 클라이언트 단(Ajax)

<form method="post" action="/upload_test.do" enctype="multipart/form-data">
    제목: <input type="text" name="title">
    파일: <input type="file" name="upfile">
    <input type="submit" value="Upload">
</form>

2. 백엔드 단(java)

package com.file;
 
import java.io.*;
import java.util.Date;
import java.text.SimpleDateFormat;
 
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import com.oreilly.servlet.MultipartRequest;
import com.oreilly.servlet.multipart.DefaultFileRenamePolicy;
 
public class Upload extends HttpServlet {
 
    /*@Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        process(req, resp);
    }*/
 
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        process(req, resp);
        
    }
    //처리내용
    protected void process(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        PrintWriter out = resp.getWriter();
        
        String fsl = File.separator;
 
        // 30Mbyte 제한
        int maxSize  = 1024*1024*30;   
        // 웹서버 컨테이너 경로
        String root = req.getSession().getServletContext().getRealPath(fsl);
        // 파일 저장 경로
        String rootPath = root + "Uploads" + fsl;
        // 업로드 파일명
        String uploadFile = "";
        // 실제 저장할 파일명
        String newFileName = "";
 
        int read = 0;
        byte[] buf = new byte[1024];
        FileInputStream fin = null;
        FileOutputStream fout = null;
        long currentTime = System.currentTimeMillis();  
        SimpleDateFormat simDf = new SimpleDateFormat("yyyyMMddHHmmss");  
     
        try{
            MultipartRequest multi = new MultipartRequest(req, rootPath, maxSize, "UTF-8", new DefaultFileRenamePolicy());
                         
            // 전송받은 parameter의 한글깨짐 방지
            String title= multi.getParameter("title");
            title= new String(savePath.getBytes("8859_1"), "UTF-8"); 
            
            // 파일업로드
            uploadFile = multi.getFilesystemName("upfile");
 
            // 실제 저장할 파일명(ex : 20140819151221.zip)
            newFileName = simDf.format(new Date(currentTime)) +"."+ uploadFile.substring(uploadFile.lastIndexOf(".")+1);     
            
            // 업로드된 파일 객체 생성
            File oldFile = new File(rootPath + uploadFile);     
             
            // 실제 저장될 파일 객체 생성
            File newFile = new File(rootPath + newFileName);             
     
            // 파일명 rename
            if(!oldFile.renameTo(newFile)){     
                //rename이 되지 않을경우 강제로 파일을 복사하고 기존파일은 삭제     
                buf = new byte[1024];
                fin = new FileInputStream(oldFile);
                fout = new FileOutputStream(newFile);
                read = 0;
                while((read=fin.read(buf,0,buf.length))!=-1){
                    fout.write(buf, 0, read);
                }
                 
                fin.close();
                fout.close();
                oldFile.delete();
            }   
     
        }catch(Exception e){
            e.printStackTrace();
        }
 
    
    }
    
}

 

HTML에서 다른 문서 import 하기

 

HTML의 기능이 막강해지면서 웹사이트를 구축할때 많은 부분이 변화되고 있다.

예전에는 웹사이트를 만들 때 개발자가 서버언어(jsp, asp, php 등)로 개발해서 디자이너가 준 html 코딩을 입히거나 반대로 디자이너가 준 html파일을 개발자가 요리조리 서버언어를 입혀서 만드는 방식이었다.

서버언어로 할 수 있는 것 중에 가장 많이 한 것이 레이아웃을 담당하고 있는 디자인의 공통 선언문들을 include 파일로 분리하여 장기간 유지보수하기 편하도록 개발하는 방식이다.

 

그런데 요즘 html의 기능들이 막강해지고 ajax, json 등의 서버 언어와 연계할 수 있는 방식들이 발달되면서 프론트엔드 페이지를 굳이 서버언어로 가지 않아도 되는 시대가 왔다. 

마치, 무겁게 입고 다니던 코트를 벗고 초경량 구스다운을 입는 것처럼 웹사이트가 매우 가벼워진 것이다.

 

그러면 이 때, 떠오르는 생각이 있다. 공통페이지들이 선언되는 것들은 어떻게 빼야 하나?

html에도 include가 지원되나??

필자 역시 순간, 그런 생각이 들었는데..

 

html에도 항상 사용하던 import 기능이 있다는 사실을.......... 잠시 망각했던 것이다.

대부분 이 기능은 css나 javascript를 직접 참조하고 싶을때 많이 사용했으므로.. 떠오르지 않았던 것 같다.

(그래.. 내가 너무 html을 무시하고 있었구나.. 뭐 그런 생각..ㅜㅜ)

 

html에서 가장 많이 참조하는 파일들은 대체로 jQuery, jQuery-mobile, Bootstrap, ignite 등등..

그 외 각종 css 까지..

 

모든 html페이지에서 이 많은 것들을 모두 다 선언하고 사용해야 하는거라면 당연히 공통파일로 빼야 한다.

그러기 위해선 아래와 같이 상단에 공통 html 파일을 선언해두고 사용하면 된다.

<head>
  <link rel="import" href="/path/to/imports/stuff.html">
</head>

그리고 그 공통파일에는 아래와 같이 쓰면 된다.

<link rel="stylesheet" href="bootstrap.css">
<link rel="stylesheet" href="fonts.css">
<script src="jquery.js"></script>
<script src="bootstrap.js"></script>
<script src="bootstrap-tooltip.js"></script>
<script src="bootstrap-dropdown.js"></script>
...


<template>
  ...
</template>

<html>, <head> 이런거 빼고 공통으로 사용할 것들만 넣어주면 끝난 것이다.

 

그런데 이것은 테스트해본 결과, IE에서는 동작하지 않았다. (정말...안타까운 일이 아닐 수 없다)

동작을 잘 하는지 알아보려면 다음과 같이 스크립트 조건문으로 확인해볼 수 있을 것이다.

if ('import' in document.createElement('link')) {
  // 지원하므로 그대로 진행합니다.
} else {
  // 파일을 로딩하기 위한 다른 라이브러리나 require 시스템들을 사용하세요.
}

만약, IE 버전에서 html 파일을 import하는 방법이 있거나 공통 레이아웃을 관리하는 페이지를 참조할 수 있는 방법을 아시는 분은 주저없이 댓글로 공유했으면 합니다. 

 

로드/에러 이벤트

<link> 엘리먼트는 import가 성공적으로 로딩되면 load이벤트가 그리고 onerror는  실패가 발생할때 발생한다.

import는 즉시 로딩을 시도하는데 즉시 로드를 피하는 방법은 아래와 같이 onload/onerror 속성을 이용해보자.

<script async>
  function handleLoad(e) {
    console.log('Loaded import: ' + e.target.href);
  }
  function handleError(e) {
    console.log('Error loading import: ' + e.target.href);
  }
</script>

<link rel="import" href="file.html"
      onload="handleLoad(event)" onerror="handleError(event)">

주의할점은, 이벤트핸들러가 페이지에서 import가 로딩되기 전에 정의되어야 한다는 점이다. 브라우저는 태그를 맞닥들이자마자 import를 시도하려 하므로 만약 함수가 아직 존재하지 않는다면 정의되지 않은 함수명에 대한 콘솔 에러가 날 수도 있기 때문이다.

 

동적으로 import를 생성하고자 한다면?

var link = document.createElement('link');
link.rel = 'import';
link.href = 'file.html'
link.onload = function(e) {...};
link.onerror = function(e) {...};
document.head.appendChild(link);

 

더 많은 html import에 대해 알아보고자 한다면 아래 링크를 타고 가세요.

출처: http://www.html5rocks.com/ko/tutorials/webcomponents/imports/

맨 위로 기능은 어떻게 줄까?

컨텐츠가 워낙 길때 스크롤을 하염없이 다시 올리기엔 벅차다.

그래서 항상 우측에 "맨 위로" 또는 "Top" 버튼을 두어 클릭하면 맨 위로 가게 해주기 때문에

사용자의 편의를 생각하면 꼭 있어야 되는 기능이다.

물론 아이폰에서는 화면 맨 위 상단을 터치하면 해당 기능을 실행해주긴 한다.

 

원래 예전에 HTML 코딩을 할 때 주로 사용했던 방식은 아래와 같이 했었다.

<a href="#top">맨 위로</a>

그러나, 위와 같이 써 주면 접근성 오류는 물론이고 웹표준에도 맞지 않다.

 

그러면 간단하게 자바스크립트로 구현해보자.

<a href="#" onClick="javascript:window.scrollTo(0,0)">맨 위로</a>

위와 같이 하면 동작이 잘 된다. 

하지만 위 방법대로 하면 <a> 태그에 #기호를 쓰기 때문에 또 웹표준 오류다.

만약 이미지로 top버튼을 준다면 아래와 같이 해보자.

<input type="button" value="맨위로" onClick="javascript:window.scrollTo(0,0)" />
<input type="image" src="top.gif" onClick="javascript:window.scrollTo(0,0)" alt="맨위로" />

이제 완벽하다. 웹표준 오류가 뜨지 않고도 맨위로 기능 맘껏 쓸 수 있다.

 

[CSS] <br> 줄간격 조절하기 : line-height

 

아래와 같이 <br>이 쓰여질 스타일에 line-height:170% 이런식으로 주면 된다. 

수치는 커질 수록 간격이 넓어진다.

<div style="line-height:170%; font-size:12px; font-family:돋움;">
   가나다라 abcd 1234<br>
   가나다라 abcd 1234<br>
   가나다라 abcd 1234<br>
   가나다라 abcd 1234<br>
</div>

 

 

자바스크립트 전화번호 형식으로 변경하기 정규식 사용

전화번호는 생각보다 다양한 형식으로 존재합니다.


1. 010-XXXX-XXXX : 11자리 휴대폰번호
2. 010-XXX-XXXX : 10자리 휴대폰번호
3. 02-XXXX-XXXX : 10자리 서울 전화번호
4. 031-XXX-XXXX : 10자리 서울 외 전화번호
5. 1688-XXXX : 8자리 업체 번호


자주 볼 수 있는 패턴을 나열하였으나 이 밖에도 아주 다양합니다.

자바스크립트에서 숫자로된 형식이 들어왔을 때
전화번호 형식으로 변경해주는 정규식 소스를 만들어봤습니다.
넘어온 타입에 따라 가운데 자리를 별표시 해주는 기능도 있으니 참고해주세요~

1. 자바스크립트 함수 생성 : phoneFomatter(num, type)
- num : '-' 문자가 들어있지않은 숫자로된 전화번호
- type : 0을 보내면 가운데자리를 숨겨준다

function phoneFomatter(num,type){
    
    var formatNum = '';
    
    if(num.length==11){
        if(type==0){
            formatNum = num.replace(/(\d{3})(\d{4})(\d{4})/, '$1-****-$3');
        }else{
            formatNum = num.replace(/(\d{3})(\d{4})(\d{4})/, '$1-$2-$3');
        }
    }else if(num.length==8){
        formatNum = num.replace(/(\d{4})(\d{4})/, '$1-$2');
    }else{
        if(num.indexOf('02')==0){
            if(type==0){
                formatNum = num.replace(/(\d{2})(\d{4})(\d{4})/, '$1-****-$3');
            }else{
                formatNum = num.replace(/(\d{2})(\d{4})(\d{4})/, '$1-$2-$3');
            }
        }else{
            if(type==0){
                formatNum = num.replace(/(\d{3})(\d{3})(\d{4})/, '$1-***-$3');
            }else{
                formatNum = num.replace(/(\d{3})(\d{3})(\d{4})/, '$1-$2-$3');
            }
        }
    }
    return formatNum;
    
}




2. 사용 예제
phoneFomatter('01000000000'); //010-0000-0000
phoneFomatter('01000000000',0); //010-****-0000
phoneFomatter('0100000000'); //010-000-0000
phoneFomatter('0100000000',0); //010-***-0000
phoneFomatter('0200000000'); //02-0000-0000
phoneFomatter('0200000000',0); //02-****-0000
phoneFomatter('0310000000'); //031-000-0000
phoneFomatter('0310000000',0); //031-***-0000
phoneFomatter('16880000'); //1688-0000


자주 사용되는거라서 한번 만들어두면 유용하게 사용할 수 있습니다.
맘껏 가지고 가셔서 쓰시되 댓글이나 좋아요는 꼭 해주세요^^

+ Recent posts