728x90

스프링부트를 공부하는 중에 타임리프에 대해 알게 되었습니다.

전에 Spring을 사용해서 팀 프로젝트를 진행할 때는 JSP를 사용했는데요.

Springboot는 뷰 템플릿으로 Thymeleaf를 사용하는 것을 권장한다고 합니다.

그래서 이 글에서는 Thymeleaf란 무엇이고, 사용할 수 있는 몇 가지 문법에 대해 정리해보겠습니다.

 

Thymeleaf

타임리프는 내츄럴 템플릿(Natural Templates)이라고 부르기도 합니다.

내추럴 템플릿은 순수 HTML을 유지하면서 뷰 템플릿으로도 사용할 수 있는 것을 말합니다.

예를 들어 JSP는 서버를 거치지 않고 파일을 열 경우에 소스코드와 HTML 코드가 섞여 알아볼 수 없습니다.

하지만 Thymeleaf는 파일을 웹 브라우저에 열어 내용을 확인할 수 있고 서버를 통해 보면 동적인 결과도 얻을 수 있습니다.

또한 JSP는 .jsp로 저장이 되고 Thymeleaf는 html 파일로 저장됩니다.

 

HTML에서 타임리프를 사용하기 위한 선언은 아래와 같습니다.

<html xmlns:th="http://www.thymeleaf.org">

해당 선언을 해줄경우 HTML 내 태그에 있는 대부분의 속성들을 th:xxx 형식으로 바꿔 사용할 수 있습니다.

html 파일을 서버를 거치지 않고 그대로 볼 때에는 기존의 html 태그의 속성이 사용되고

서버를 거쳐 동적으로 뷰 템플릿을 거치게 되면 기존 태그의 속성을 th:xxx의 값으로 치환합니다.

 

예를 들어 input의 value값을 Thymeleaf로 표현하면 아래와 같습니다.

<!-- 기존 HTML -->
<input type="text" value="1"/>

<!-- Thymeleaf -->
<input type="text" value="1" th:value="${otherValue}"/>

이렇게 작성된 html은 html 파일을 웹브라우저로 켤 경우 input 박스의 값이 1로 보이고

서버를 거쳐 렌더링되어 웹브라우저에 보일 경우에는 otherValue에 담긴 값이 input박스의 값으로 나오게 됩니다.

 

간단하게 Thymeleaf에 대해 설명을 해봤습니다.

아래에는 타임리프의 간단한 문법을 정리해보겠습니다.

 

문법

URL 링크 표현식

<a href="/user" th:href="@{/user}"/>

타임리프에서 링크표현식은 @{...}을 사용합니다. 

링크 표현식은 표현식 안에 '()'를 사용해서 경로 변수와 쿼리 파라미터도 생성 가능합니다. 

<!-- 경로 변수
{itemId}를 item의 id로 대체합니다.
-->
th:href="@{/basic/items/{itemId}(itemId=${item.id})}"

<!-- 쿼리 파라미터 -->
th:href="@{/basic/items/{itemId}(itemId=${item.id}, query='test')}"
<!-- 지정되지 않은 query는 쿼리 파라미터로 치환됩니다.
	 basic/items/1?query=test
-->

 

물론 간단히 아래처럼 사용할 수도 있습니다.

th:href="@{|/basic/items/${item.id}|}"

 

리터럴 대체

 

리터럴 대체는 |...|으로 사용합니다.

타임리프에서는 문자와 표현식 등이 분리되어 있기 때문에 + 연산자를 사용해야 합니다.

하지만 리터럴 대체 문법을 사용하면 연산자를 사용할 필요 없이 사용할 수 있습니다.

<!-- 리터럴 대체 사용 전 -->
<span th:text="'Welcome to our application, ' + ${user.name} + '!'">

<!-- 리터럴 대체 사용 후 -->
<span th:text="|Welcome to our application, ${user.name}!|">

 

변수 표현식

 

변수 표현식은 ${...}으로 사용합니다.

모델에 포함된 값이나 타임리프 변수로 선언한 값을 조회할 수 있습니다.

프로퍼티 접근법을 사용합니다. 

${param}
// ex)
// localhost:8080/user?userId=kkkt
${param.userId} => kkkt

위 코드는 타임리프에서 쿼리 파라미터를 편리하게 조회할 수 있게 저장된 예약어입니다.

그래서 param뒤에 쿼리 파라미터의 키를 넣으면 값을 손쉽게 사용할 수 있습니다.

 

th:each

 

반복문을 사용할 때 쓰는 문법입니다.

<tr th:each="item : ${items}">

위 코드처럼 사용하는데요 items에 담겨있는 데이터를 하나씩 item 변수에 담아 사용합니다

JSTL의 forEach 구문과 같은 역할을 합니다.

 

 

 

추가적인 문법이 알고 싶다면 타임리프 공식 사이트를 확인해보세요

https://www.thymeleaf.org/doc/tutorials/3.0/thymeleafspring.html#listing-seed-starter-data

728x90
728x90

 

웹 서비스를 만들기 위해서는 클라이언트와 서버 간에 데이터를 주고받을 필요가 있습니다.

그리고 REST API같은 HTTP API를 제작하기 위해서도 데이터의 처리방법에 대해 알 필요가 있습니다.

이전에 스프링을 사용하지 않고 이클립스 IDE만 사용해서 컨트롤러를 만들어 봤었습니다.

이때에는 매번 메서드에 HttpServletRequest를 매개변수로 사용하고 .getParameter()를 사용해

데이터를 처리하느라 좀 귀찮은 점이 있었습니다. 하지만 스프링을 사용하면 HTTP 요청과 응답 데이터를

쉽게 처리할 수 있습니다. 

 

우선 요청 데이터를 처리하는 것에 대해 정리하고 응답 데이터 처리에 대해 정리해보겠습니다.

응답 데이터 처리는 요청 데이터 처리와 중복되는 것이 많기 때문에 간략하게 해보겠습니다.

 

HTTP 요청 데이터 조회

 

HTTP 요청 메시지를 통해 클라이언트에서 서버로 데이터를 전달하는 방법은 주로 세 가지 방법을 사용합니다.

  • GET - 쿼리 파라미터
  • POST - HTML Form
  • HTTP message body

GET과 POST의 HTML Form 데이터를 처리하는 방법은 같습니다.

그전에 GET과 POST의 데이터 전달 방법에 대해 먼저 알아보겠습니다.

일단 GET의 데이터는 메시지 바디 없이 URL의 ?뒤에 Key=Value의 형식으로 전달합니다.

ex) localhost:8080/user?username=kim&userage=22

POST의  Form 데이터는 메시지 바디로 전달하지만 형식은 똑같이 쿼리 파라미터 형식으로 전달합니다.

username=kim&userage=22

이때 HTTP 헤더의 content-type은 application/x-www-form-urlencoded입니다.

 

이렇게 쿼리 파라미터 형식으로 전달하는 두 데이터를 처리하는 방법에는 크게 세 가지가 있습니다.

앞서 이야기했던 HttpServletRequest의 .getParameter를 사용하는 방법과

@RequestParam을 사용하는 방법 그리고 @ModelAttribute를 사용하는 방법이 있습니다.

 

@RequestParam

 

해당 어노테이션은 해당 어노테이션에 값으로 설정한 데이터가 들어오면 매핑해주는 역할을 합니다.

그리고 파라미터의 이름과 데이터의 Key값이 동일하면 어노테이션을 생략할 수 있습니다.

// URL -> http://localhost:8080/request-param?username=kim&age=24

// 1번 방법
@RequestMapping("/request-param")
public String requestParamV2(
	@RequestParam("username") String memberName,
	@RequestParam("age") int memberAge){...}

// 2번 방법 어노테이션 안의 값 생략
@RequestMapping("/request-param")
public String requestParamV2(
	@RequestParam String username,
 	@RequestParam int age){...}

// 3번 방법 어노테이션 자체를 생략
@RequestMapping("/request-param")
public String requestParamV2(String username,int age){...}

 

추가적으로 @RequestParam는 required 속성을 가지고 있습니다.

디폴트 값이 true인데 이럴 경우 해당하는 파라미터의 요청 데이터가 넘어오지 않으면 400 에러를 반환합니다.

하지만 ""처럼 빈 값을 넘겨도 아무런 에러를 반환하지 않습니다.

그리고 기본형 타입의 파라미터에 required=false를 설정했을 때는 값이 넘어오지 않으면 null값을 주는 것이 불가능하여

500 에러를 반환하기 때문에 이를 처리하는 것이 필요합니다.

이때에는 다른 속성인 defaultValue나 래퍼클래스를 사용하면 됩니다.

// required 속성 사용
@RequestMapping
public String requestParamDefault(
        @RequestParam(required = true) String username,
        @RequestParam(required = false) int age) {...} 
        // age 해당하는 값이 안넘어 올 경우 500에러를 반환한다.

// defaultValue 속성 사용
@RequestMapping
public String requestParamDefault(
		@RequestParam(defaultValue = "guest") String username,
		@RequestParam(defaultValue = "-1") int age){...}
        
// 래퍼클래스 사용
@RequestMapping
public String requestParamDefault(
		@RequestParam String username,
		@RequestParam(required = false) Integer age){...}
        // 래퍼클래스를 사용해 null값 오류를 해결할 수 있다.

 

@ModelAttribute

 

스프링은 객체에 요청 파라미터를 바인딩하는 기능도 가지고 있습니다.

이게 @ModelAttribute의 기능입니다. 해당 어노테이션도 생략이 가능합니다.

@RequestMapping
public String modelAttributeV1(@ModelAttribute HelloData helloData) {...}


// @ModelAttribute 생략
@RequestMapping
public String modelAttributeV1(HelloData helloData) {...}

위와 같이 작성 가능합니다.

스프링MVC는 @ModelAttribute가 있으면 다음을 실행합니다.

  • HelloData 객체를 생성합니다.
  • 요청 파라미터의 이름으로 HelloData 객체의 프로퍼티를 찾습니다.
  • 해당 프로퍼티의 setter를 호출해 파라미터의 값을 바인딩합니다.

때문에 객체로 사용할 클래스에는 setter 메서드를 만들어 둬야 합니다.

 

앞서 이야기한 @RequestParam과 @ModelAttribute는 둘 다 생략할 수 있습니다.

때문에 스프링은 생략 시 다음과 같은 규칙을 적용합니다.

  • String, int, Integer와 같은 단순 타입은  @RequestParam을 사용합니다.
  • argument resolver로 지정해둔 타입을 제외한 나머지는 @ModelAttribute 사용합니다.

 

HTTP Message Body

 

다음은 HTTP 메시지 바디에 담긴 단순 텍스트 데이터를 처리하는 방법입니다.

HTTP API에 주로 사용되고 JSON 형식이나 XML 형식을 주로 사용합니다.

 

이렇게 메시지 바디로 넘어오는 데이터는 앞서 이야기한 @RequestParam과 @ModelAttribute를 사용할 수 없습니다.

HTML form으로 넘어오는 데이터를 제외하고요.

요청 메시지 바디로 넘어오는 데이터를 조회하는 방법은 아래와 같습니다. 

  • InputStream
  • HttpEntity
  • RequestEntity - HttpEntity를 상속받은 클래스로 추가 기능을 제공합니다.
  • @RequestBody

 

이중에서는 @RequestBody를 사용하는 게 가장 편해 보였습니다.

스트림은 바이트 타입으로 넘어오기 때문에 다시 한번 타입에 맞춰 변경해주는 작업이 필요합니다.

그리고 HttpEntity나 @RequestBody는 HTTP 메시지 컨버터를 통해 바디의 내용을 문자나 객체 등으로 변환시켜줍니다.

하지만 HttpEntity는 getBody()로 한 번 더 꺼내는 과정이 필요합니다. 

// @RequestBody
@PostMapping("/request-body-json")
public HelloData requestBodyJsonV5(@RequestBody HelloData data) {...}

// HttpEntity
@PostMapping("/request-body-json")
public String requestBodyJsonV4(HttpEntity<HelloData> httpEntity) {...}

 

HTTP 응답

 

스프링에서 응답 데이터를 만드는 방법은 크게 세 가지입니다.

  • 정적 리소스
  • 뷰 템플릿 사용
  • HTTP 메시지 사용

 

 

HTTP 메시지 사용

 

HTTP API나 비동기 통신을 통해 데이터의 특정 값만 제공해야 하는 경우에는

HTTP 바디에 직접 데이터를 실어서 보낼 필요가 있습니다.

 

만약 컨트롤러의 메서드가 전부 특정 데이터만 전달해야 하는 경우에는 클래스에 

@RestController나 @ResponseBody 어노테이션을 달아 사용할 수 있습니다. 

@RestController는 @Controller와 @ResponseBody 합쳐진 것이기 때문에 둘 중 무엇을 사용하든 상관없습니다.

 

스프링의 경우 String으로 반환하면 뷰 리졸버가 실행되어서 뷰를 찾고 렌더링 합니다.

하지만 @ResponseBody가 달린 클래스나 메서드는 반환 값을 HTTP 응답 메시지 바디에 직접 실어 보냅니다.

그리고 해당 어노테이션을 쓰지 않고 HttpEntity나 ResponseEntity를 사용해서 응답할 수도 있습니다.

ResponseEntity를 사용할 경우 HTTP 응답 코드를 설정할 수 있는데 @ResponseBody는 따로 설정할 수 없습니다.

하지만 @ResponseStatus를 사용한다면 @ResponseBody로 반환하는 메서드도 응답 코드를 설정할 수 있습니다.

 

// @ResponseBody와 @ResponseStatus 사용
@ResponseStatus(HttpStatus.OK)
@ResponseBody
@GetMapping("/response-body-json")
public HelloData responseBodyJson() {
    HelloData helloData = new HelloData();
    helloData.setUsername("userA");
    helloData.setAge(20);
    
    return helloData;
}

// ResponseEntity 사용
@GetMapping("/response-body-json")
public ResponseEntity<HelloData> responseBodyJson() {
    HelloData helloData = new HelloData();
    helloData.setUsername("userA");
    helloData.setAge(20);
    
    return new ResponseEntity<>(helloData, HttpStatus.OK);
}

 

 

참조

HTTP 메시지 컨버터

argument resolver

728x90
728x90

최근에 스터디를 하면서 공부를 하고 있습니다.

그런데 금방 까먹는 듯한 기분이 들어 정리하는 시간을 가지려고 합니다.

이렇게 하면 더 오래 남지 않을까 하는 개인적인 기대가 있습니다.

이번 글은 인프런 김영한 님의 스프링 강의 - 스프링의 기본 기능 내용 정리입니다.

 

로깅(Logging)

제가 프로젝트를 수행할 때는 어떠한 버그가 발생했을 때 이유를 알아보기 위해서 

시스템 콘솔에 데이터를 출력하는 System.out.println()을 사용했습니다.

하지만 실무에서 위의 방법을 사용할 경우에는 로그관리를 할 수 없기 때문에 별도의 로깅 라이브러리를 사용합니다.

스프링 부트는 기본적으로 slf4j라는 라이브러리를 사용하는데,

이는 Logback, Log4j, Log4j2 등 수많은 라이브러리를 통합해서 제공하는 라이브러리입니다.

 

로그의 선언에는 

private Logger log = LoggerFactory.getLogger(getClass());
private static final Logger log = LoggerFactory.getLogger(Xxx.class);
@Slf4j

위와 같은 방법을 사용할 수 있습니다. Logger와 LoggerFactory은 slf4j의 클래스입니다.

그리고 @Slf4j 어노테이션은 Lombok 라이브러리의 어노테이션으로 위의 두줄과 같이 직접적인 선언 없이

로그를 호출할 수 있게 해 줍니다. 이외에도 Lombok은 DTO 클래스에서 setter, getter의 작성을 쉽게 해주기도 합니다.

Lombok에 대한 자세한 내용은 다음에 다뤄보도록 하겠습니다.

 

 호출은 아래와 같이 할 수 있습니다.

String name = "Spring";
 
 log.trace("trace log={}", name);
 log.debug("debug log={}", name);
 log.info(" info log={}", name);
 log.warn(" warn log={}", name);
 log.error("error log={}", name);

로그의 출력은 개발 단계와 운영 단계에 맞춰 각각 출력하는 범위를 정할 수 있습니다.

레벨 순은 trace >> debug >> info >> warn >> error 순으로 디폴트 값은 info입니다.

그래서 출력을 info로 설정하면 하위 레벨인 debug와 trace는 출력되지 않습니다.

 

log.debug("String concat log=" + name);

그리고 로그의 출력을 위와 같이 + 연산자를 사용해서 출력할 수도 있습니다.

하지만 위와 같은 경우는 리소스를 낭비하여 사용하지 않아야 합니다.

출력이 info로 설정되어 있을 경우 debug는 대상이 아니라 그냥 넘어가야 하지만

그 작업이 이뤄지기 이전에 + 연산자의 로직이 실행돼 불필요한 자원을 소모하기 때문입니다.

 

로그 사용의 장점

  • 스레드 정보, 클래스 이름 같은 부가 정보를 함께 볼 수 있고, 출력 모양을 조정할 수 있습니다.
  • 로그를 상황에 맞게 레벨을 조절하여 출력할 수 있습니다.
  • 파일이나 네트워크 등, 로그를 별도의 위치에 남길 수 있습니다.
  • 파일로 남길 때에는 특정 기준에 맞춰 로그를 분할할 수 있습니다. 

 

매핑(Mapping)

스프링에서는 @Controller 어노테이션을 사용해 컨트롤러를 찾을 수 있습니다.

그리고 @RequestMapping 어노테이션을 사용해 해당하는 URL 호출이 오면 메서드가 실행되도록 매핑할 수 있습니다.

여기서는 @RequestMapping의 여러 매핑 방법에 대해 정리해보도록 하겠습니다.

 

@RequestMapping 방법 

  • HTTP 메서드 매핑
  • PathVariable 매핑
  • 특정 파라미터 조건 매핑
  • 특정 헤더 조건 매핑
  • 미디어 타입 조건 매핑

 

HTTP 메서드 매핑

 

HTTP 메서드 매핑은 @RequestMapping의 method 속성으로 매핑을 하는 방법입니다.

기본적으로 GET, POST, PUT, PATCH 등 모든 메서드에 대해 허용하게 됩니다.

method 속성을 사용하면 이를 특정 메서드의 요청에만 응답하도록 설정할 수 있습니다.

@RequestMapping(value = "/mapping", method = RequestMethod.GET)

위 URL에 POST 요청을 보내고 POST 요청에 해당하는 매핑이 없을 경우 

스프링 MVC는 HTTP 405(Method Not Allowed) 코드를 반환합니다.

덧붙여 스프링 부트는 해당 메서드 매핑의 축약형 어노테이션을 제공합니다.

@GetMapping(value="/mapping")
@PostMapping(value="/mapping")
@PutMapping(value="/mapping")

 

 

PathVariable 매핑

 

HTTP 리소스 경로에 식별자를 넣는 방법입니다.

@GetMapping("/mapping/{userId}")
public String mappingPath(@PathVariable("userId") String data) {}

@GetMapping("/mapping/{userId}")
public String mappingPath(@PathVariable String userId) {}

위와 같이 사용할 수 있으며, 경로 변수와 파라미터의 이름이 같다면 @PathVariable의 이름을 생략할 수 있습니다.

 

특정 파라미터 조건 매핑

 

/**
 * 파라미터로 추가 매핑
 * params="mode",
 * params="!mode"
 * params="mode=debug"
 * params="mode!=debug" (! = )
 * params = {"mode=debug","data=good"}
 */
@GetMapping(value = "/mapping-param", params = "mode=debug")

특정 파라미터가 있거나 없는 요청에 대해서 매핑하는 방법입니다.

하지만 잘 사용되지는 않는다고 합니다.

 

 

특정 헤더 조건 매핑

 

/**
 * 특정 헤더로 추가 매핑
 * headers="mode",
 * headers="!mode"
 * headers="mode=debug"
 * headers="mode!=debug" (! = )
 */
@GetMapping(value = "/mapping-header", headers = "mode=debug")

파라미터 매핑과 비슷하지만 HTTP 헤더를 이용합니다.

 

 

미디어 타입 조건 매핑

 

미디어 타입 조건 매핑에는 HTTP 요청의 Content-Type과 Accept 해당하는 매핑이 있습니다.

Content-Type은 클라이언트가 서버에게 전송하는 데이터의 원래 미디어 유형을 말합니다.

Accept는 클라이언트가 이해할 수 있는 미디어 유형의 HTTP 요청 헤더입니다. 

 

특정 Content-Type 요청에는 consumes 속성을 사용하고, Accept 요청에는 produces 속성을 사용합니다.

제가 이 속성들을 사용할 때에는 주로 jQuery로 Ajax 통신을 할 때 사용했습니다.

특정한 값으로 전달받거나 전달하기 위해서 그렇습니다.

 

Content-Type

/**
 * Content-Type 헤더 기반 추가 매핑 Media Type
 * consumes="application/json"
 * consumes="!application/json"
 * consumes="application/*"
 * consumes="*\/*"
 * MediaType.APPLICATION_JSON_VALUE
 */
@PostMapping(value = "/mapping-consume", consumes = "application/json")

/**
 *
 * consumes 예시
 */
consumes = "text/plain"
consumes = {"text/plain", "application/*"}
consumes = MediaType.TEXT_PLAIN_VALUE

맞지 않으면 HTTP 415(Unsupported Media Type) 상태 코드를 반환합니다.

 

Accept

/**
 * Accept 헤더 기반 Media Type
 * produces = "text/html"
 * produces = "!text/html"
 * produces = "text/*"
 * produces = "*\/*"
 */
@PostMapping(value = "/mapping-produce", produces = "text/html")

/**
 *
 * produces 예시
 */
produces = "text/plain"
produces = {"text/plain", "application/*"}
produces = MediaType.TEXT_PLAIN_VALUE
produces = "text/plain;charset=UTF-8"

맞지 않으면 HTTP 406(Not Acceptable) 상태코드를 반환합니다.

728x90
728x90

MyBatis Mapper XML파일

태그

  • 구문 태그 INSERT, UPDATE, DELETE, SELECT
  • sql - 다른 구문에서 재사용하기 위한 조각
  • cache - 해당 네임스페이스를 위한 캐시 설정
  • cache-ref - 다른 네임스페이스의 캐시 설정에 대한 참조
  • resultMap - 데이터베이스 결과 데이터를 객체에 로드하는 방법을 정의하는 엘리먼트
  • selectKey - 구문 태그의 실행 전이나 후에 실행할 구문을 지정한다.
  • => keyProperty 구문의 결과가 세팅될 프로퍼티
  • => order (BEFORE, AFTER)를 세팅할 수 있다. 말 그대로 sql 구문의 이전과 이후

태그의 속성

  • parameterType - SQL 구문에 사용할 파라미터 타입을 지정한다. 
  • => MyBatis의 경우 파라미터를 하나만 받아올 수 있기 때문에 객체로 받아오던지 hashmap을 사용한다.
  • => 객체를 넘길 경우 패키지 경로를 포함한 전체 클래스명이나 별칭을 넘겨야 한다. 
  • => 파라미터 표기법 #{} ex) int midx를 받아 온 경우 #{midx}
  • resultType - SQL 구문 작동 후 DB에서 받아오는 결과 타입
  • => 객체를 넘길 경우 패키지 경로를 포함한 전체 클래스명이나 별칭을 넘겨야 한다.

MyBatis CDATA

sql 구문에서 조건문과 같은 비교 연산자를 사용할지 mybatis에서

이를 문자열로 인식하지 않아 오류가 발생할 수 있다.

때문에 비교 연산자와 같이 문자가 아닌 특수문자를 사용할 때에는

오류를 피하고 정확한 구문을 사용하기 위해 CDATA 안에 쿼리를 작성한다.

 

jQuery serialize 메서드

form 태그에서 name 속성을 가진 태그들의 데이터를 문자열로 반환해준다.

<script>
	function Log(){
    	console.log($("#fm").serialize())
    }
</script<
<form id="fm">
	<input type="text" name="id">
    <input type="text" name="pwd">
    <input type="text" name="name">
    <input type="button" onclick="Log()" value="등록">
</form>

<!-- 출력되는 값 -->
<!-- id=input id에 입력한 값&pwd=input pwd에 입력한 값&name= input name에 입력한 값 -->
728x90
728x90

만들려는 사이트가 주식 커뮤니티인 만큼 자신의 자산 추이를 한눈에 볼 수 있는 그래프를 추가하고 싶었는데

그 방법으로 구글차트를 이용해 보겠습니다.

 

 

그래프 하단에 날짜를 지정하고 자산을 입력한 다음 추가 버튼을 누르면 그 정보가 데이터베이스 입력되고 동시에 

그래프와 하단의 목록에 뜨게됩니다.

우선 구글 차트에 들어가서 원하는 차트 타입을 누릅니다.

https://developers.google.com/chart/interactive/docs

저는 선 그래프를 이용했기 때문에 line chart로 설명을 하겠습니다.

왼쪽에 line charts를 눌러주시고 Code it yourself on JSPFiddle을 누르면

예제 코드를 볼 수 있고 데이터도 바꾸면서 확인할 수 있습니다. 

 

html의 script를 본인 html head태그 안에 넣어주시고 

div 태그는 그래프가 그려질 장소이니 그래프를 보여줄 곳에 넣어주면 됩니다.

 

google.charts.load('current', {packages: ['corechart', 'line']});
google.charts.setOnLoadCallback(drawchart);
function drawchart() {
      var data = new google.visualization.DataTable();
      data.addColumn('number', 'X');
      data.addColumn('number', 'Dogs');

      data.addRows([ ]);

      var options = { };

      var chart = new google.visualization.LineChart(document.getElementById('chart_div'));

      chart.draw(data, options);
    }

구글 차트의 기본적인 구조입니다. 

차트를 로드하고 .setOnLoadCallback(); 함수로 html이 전부 로드된 후 매개변수에 들어간 함수를 호출합니다.

data 변수에 데이터 테이블을 생성하고 .addColumn으로 열을 추가합니다

열의 데이터 타입은 많이 있으니 직접 검색해보시면 됩니다.  

.addRows는 만들어진 열에 데이터를 추가하는 것으로 배열 형식으로 추가할 수 있습니다.

options는 그래프의 옵션을 정하는 변수로 x축, y축, 그래프 사이즈 등 여러 옵션을 설정할 수 있습니다.

chart에 그래프가 그려질 곳을 설정하고 .draw로 데이터와 옵션을 추가해 그립니다.

 

저는 oracle db에 있는 데이터를 받아와 출력할 생각이기 때문에 .addRow만 좀 다르게 해 주면 됩니다.한 번에 그리는 방법도 있을 수 있는데 저는 제 실력이 부족해서 한번에 데이터를 받아와 그리는 것을 못했습니다.그래서 html이 전부 그려지고  빈 차트를 그린 후에 거기에 데이터를 추가하는 방법을 선택했습니다.

google.charts.load('current', {packages: ['corechart', 'line'], callback: drawBasic});	
google.charts.setOnLoadCallback(addData);
var data;
var chart;
var options;
function drawBasic() { 
      data = new google.visualization.DataTable();
      data.addColumn('datetime', 'year');
      data.addColumn('number', 'Property');

      data.addRows([
      ]);
      options = {
              width:'100%'
      }

      chart = new google.visualization.LineChart(document.getElementById('chart_div'));
      chart.draw(data, options);
    }

먼저 빈 차트를 그리는 코드입니다.

변수를 두 번째에 그릴 때도 사용할 것이기 때문에 함수 밖에 변수를 설정해줍니다.

저는 x축을 날짜별로 보여줄 거라 데이터 타입을 datetime으로 설정했습니다.

data 행은 두 번째 그릴 때 추가할 거라 빈 값으로 설정해주고 그려주면 끝납니다.

 

var year = [];
var month = [];
var day = [];
var value = [];	 	
<%for(GraphVO gv : glist){%>
year.push(<%=gv.getYear()%>);
month.push(<%=gv.getMonth()%>);
day.push(<%=gv.getDay()%>);
value.push(<%=gv.getMoney()%>);
<% }%>
var len = year.length;
function addData(){
    var arr = new Array();
    for(let i = 0; i<len; i++){
         arr = [new Date(year[i],month[i]-1,day[i]),value[i]];
         data.addRow(arr);
     }
    options = {
            pointSize: 5,
            width:'100%',
            hAxis: {
              title: 'Time',
              format:'yy-MM',
              ticks: [new Date(year[0],month[0]-1,day[0]),new Date(year[len-1],month[len-1]-1,day[len-1])]
            },
            explorer: { axis: 'horizontal' }
          };
    chart.draw(data, options);
}

데이터를 전부 출력하기 위해 컨트롤러에서 받아온 데이터를 변수 배열에 추가해주시고요

for문으로 위에 설정해둔 data 변수에 .addRow해서 일일이 추가해줍니다.

원하는 옵션도 추가해주시고 다시 그려주면 끝납니다.

 

더 쉬운 방법도 물론 있을 수 있으나 처음 사용하다 보니 미숙해서

빈 차트에 새로운 데이터를 덮어 씌우는 방법을 사용해봤습니다.

 

 

아래는 전체 코드입니다.

전체 코드 - https://github.com/seohakman/stockCommu/blob/main/src/main/webapp/member/FinancialGraph.jsp

 

GitHub - seohakman/stockCommu: 커뮤니티 사이트 만들기/ 개인 프로젝트

커뮤니티 사이트 만들기/ 개인 프로젝트. Contribute to seohakman/stockCommu development by creating an account on GitHub.

github.com

 

728x90
728x90

글쓰기 페이지는 로그인했을 때만 이동할 수 있게 만들어뒀다.

제목과 내용을 작성하고 첨부파일을 하나 넣을 수 있다.

 

 

아래는 jsp에서 컨트롤러로 값을 보내는 코드

이미지 사이즈를 재설정하는 imgscalr-lib와 첨부파일 기능을 만들기 위해서 cos.jar 라이브러리를 다운로드하여 사용했다.

파일을 업로드하기 위해서 form enctype 속성을 "multipart/form-data"로 설정해주었다.

이렇게 설정해주면 폼 값을 보낼때 인코딩하지 않고 값을 보낸다.

// 컨트롤러로 내용을 보낸다. 
<script>
    function mainSubmit(){
        document.fm.action="<%=request.getContextPath()%>이동할 컨트롤러";
        document.fm.method="post";
        document.fm.enctype="multipart/form-data"; 
    }
</script>
<form name=fm>
<table>
    <thead>
        <tr>
            <th><input id="subject" name="subject" type="text" placeholder="제목을 입력하세요" required></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td><textarea id="content" name="content" placeholder="내용을 입력하세요" required></textarea></td>
        </tr>
        <tr>
            <td id="filePath"> <input name="filename" type="file"> </td>
        </tr>
    </tbody>
    <tfoot>
        <tr>
            <td>
                <button type="submit" onclick="mainSubmit()">등록</button>
                <button type="button" onclick="history.back();">취소</button>
            </td>
        </tr>
    </tfoot>
</table>
</form>

아래는 컨트롤러에서 글을 DB에 등록하는 코드

sizeLimit는 등록하는 파일의 용량을 제한한다. 이미지 파일일 경우 이렇게 하면 너무 크다...

이전에 form에서 데이터 타입을 multipart/form-data 보냈기 때문에 이전의 request 영역에서 값을 읽어오는 것과 다르게

MultipartRequest 객체를 생성해 여기서 값을 읽는다.

//작성한 글을 DB에 넣는다.
int sizeLimit = 1024*1024*15;
MultipartRequest multi = new MultipartRequest(request, saveFullPath, sizeLimit, "utf-8", new DefaultFileRenamePolicy());

HttpSession session = request.getSession();
String subject = multi.getParameter("subject");
String content = multi.getParameter("content").replace("\r\n", "<br>");
String id = (String) session.getAttribute("id");
int midx = (int)session.getAttribute("midx");

// 열거자에 저장될 파일을 담는 객체를 생성한다.
Enumeration files = multi.getFileNames();
// 담긴 파일객체의 파일 이름을 얻는다.
String file = (String)files.nextElement();
//저장되는 파일 이름
String fileName = multi.getFilesystemName(file);
//원래 파일 이름
String originFileName = multi.getOriginalFileName(file);

MainDAO mdo = new MainDAO();
int value = mdo.insertMain(subject, content, id, midx, fileName);
PrintWriter out = response.getWriter();
if(value == 1) {
    response.sendRedirect(pj+"/main/index.do");
}else{
    out.println("<script>alert('글쓰기 실패');</script>");
}

아래는 DAO의 입력 메서드

public int insertMain(String subject, String content, String ID, int midx, String fileName) {
    // DB에 글을 넣는다.
    int value = 0;
    String sql = "insert into main(bidx,subject,content,midx,writer,viewcount,likecount,filename)" 
    	+ "values(bidx_main.nextval,?,?,?,?,?,?,?)";

    try {
        pstmt = conn.prepareStatement(sql);
        pstmt.setString(1, subject);
        pstmt.setString(2, content);
        pstmt.setInt(3, midx);
        pstmt.setString(4, ID);
        pstmt.setInt(5, 0);
        pstmt.setInt(6, 0);
        pstmt.setString(7, fileName);
        value = pstmt.executeUpdate();

    } catch (SQLException e) {
        e.printStackTrace();
    }
    return value;
}
728x90
728x90

제가 만든 커뮤니티 사이트의 로그인 페이지 입니다. 

기본적인 로그인 기능과 아이디/ 비밀번호 찾기 기능도 구현했습니다.

로그인 버튼으로 데이터를 전송하려면 아이디와 비밀번호 input 박스에 데이터를 필수적으로 작성해야 합니다.

아래는 로그인 기능의 jsp 코드입니다.

<script>
	function clickLogin(){
		document.fm.action="#이동할 컨트롤러";
		document.fm.method="post";
	}
</script>
<form name=fm>
<table>
    <tr>
      <td>아이디 :</td>
      <td><input type="text" required name="ID"></td>
    </tr>
    <tr>
      <td>비밀번호 :</td>
      <td><input type="password" required name="PWD"></td>
    </tr>
    <tr>
      <td colspan="2">
        <a href="#이동할 컨트롤러">아이디찾기</a>  /
        <a href="#이동할 컨트롤러">비밀번호찾기</a>
      </td>
    </tr>
    <tr>
      <td colspan="2"><a href="#이동할 컨트롤러">회원가입</a></td>
    </tr>
    <tr>
      <td colspan="2">
        <button type="submit" id="login-btn" onclick="clickLogin()"><span>로그인</span></button>
      </td>
    </tr>
</table>
</form>

아래는 servlet 컨트롤러에서 받아오는 파라미터와 로그인 기능을 수행하는 메서드입니다.

// 컨트롤러
// 로그인 버튼이 눌렷을때 로그인 기능을 동작시킨다.
// 1. 넘어온 값을 받는다.
String id = request.getParameter("ID");
String pwd = request.getParameter("PWD");
// 2.처리 (쿼리실행)
MemberDAO md = new MemberDAO();
MemberVO mv = null;
mv = md.memberLogin(id, pwd);
PrintWriter out = response.getWriter();

// 세션에 로그인할 때 받아온 값을 저장해 다른 페이지로 이동할 때에도 
// 로그인 상태를 유지하게한다.
HttpSession session = request.getSession();
if(mv != null) {
    session.setAttribute("midx", mv.getMidx());
    session.setAttribute("id", mv.getId());
    session.setAttribute("name", mv.getName());
    session.setAttribute("point", mv.getPoint());
    session.setAttribute("superMember", mv.getSupermember());
    session.setAttribute("password", mv.getPwd());
    // 3.이동
    // saveUrl이라는 로그인 페이지로 이동하기 전 url이 있을경우 그 페이지로 이동
    // 없을 경우 인덱스 페이지로 이동
    if(session.getAttribute("saveUrl") != null) {
        response.sendRedirect((String)session.getAttribute("saveUrl"));
    }else {
        response.sendRedirect(request.getContextPath()+"/main/index.do");
    }
}else {
    out.println("<script>alert('아이디, 비밀번호가 틀렸거나 존재하지 않는 회원입니다.');location.href='"+request.getContextPath()+"/member/memberLogin.do'</script>");

}

// DB에 로그인 기능을 실행하는 메서드
public MemberVO memberLogin(String ID, String PWD) {
    // id와 pwd를 파라미터로 받아온다.
    String sql = "select * from member where id = ? and pwd =?";
    ResultSet rs = null;
    MemberVO mv = null;
    try {
        pstmt = conn.prepareStatement(sql);
        pstmt.setString(1, ID);
        pstmt.setString(2, PWD);
        rs = pstmt.executeQuery();

        if(rs.next()) {
            mv = new MemberVO();
            mv.setMidx(rs.getInt("midx"));
            mv.setName(rs.getString("name"));
            mv.setId(rs.getString("id"));
            mv.setPwd(rs.getString("pwd"));
            mv.setSupermember(rs.getString("supermember"));
        }
    } catch (SQLException e) {
        e.printStackTrace();
    }finally {
        try {
            rs.close();
            pstmt.close();
            conn.close();
        } catch (SQLException e) {

            e.printStackTrace();
        }
    }

    return mv;
}

 


아이디 찾기와 비밀번호 찾기는 같은 로직을 가지고 있다고 보면 됩니다.

아이디를 찾을 때 DB에서 이름과 이메일이 일치하는 값이 있을 경우에 ID를 받아와서 보여주면 되는데

비밀번호의 경우 일치하는 값을 추가하거나 일치했을 때 새로운 비밀번호를 설정하게 하는 등 기존의 로직에서 새로운 방법을 추가하는 것입니다.

jsp에서 form으로 이름과 이메일을 컨트롤러에 파라미터로 넘겨줍니다.

컨트롤러에서는 받은 파라미터를 다시 변수로 설정하고 만들어둔 메서드를 통해

DB와 일치하는 값이 있는지 비교하고 일치하는 값이 있을 경우 결과값을 받아옵니다.

이 값을 다시 웹페이지에 출력해주면 됩니다. 

//아이디 찾기 jsp
<script>
	function findIDbtn(){
		document.fm.action="<%=request.getContextPath()%>이동할 컨트롤러";
		document.fm.method="post";
	}
</script>
<form name=fm>
<table>
    <tr>
      <td>이름 :</td>
      <td><input type="text" name="name" required></td>
    </tr>
    <tr>
      <td>이메일 :</td>
      <td><input type="text" name="email" required></td>
    </tr>
    <tr>
      <td colspan="2">
        <button type="submit" id="findID-btn" onclick="findIDbtn()"><span>아이디 찾기</span></button>
      </td>
    </tr>
</table>
</form>

아래는 컨트롤러와 메서드 코드

sql 구문을 통해 DB에서 값을 검색하는데 여기서 table 이름과 column만 본인걸로 바꿔서 적용하면 될겁니다.

// 컨트롤러
// 아이디찾기 버튼을 클릭했을 때
PrintWriter out = response.getWriter();
String name = request.getParameter("name");
String email = request.getParameter("email");

MemberDAO md = new MemberDAO();
MemberVO mv = null;
mv = md.findID(name, email); // 아이디를 찾는 메서드
if(mv != null) {
    out.println("<script>alert('"+name+"님의 아이디는 "+mv.getId()+"입니다.');"
            + "location.href='"+request.getContextPath()+"이동할 컨트롤러'</script>");
}else {
    out.println("<script>alert('아이디가 존재하지 않습니다.');"
            + "location.href='"+request.getContextPath()+"이동할 컨트롤러'</script>");
}

//메서드
public MemberVO findID(String name, String email) {
    //아이디 찾기 메서드
    String sql = "select * from member where name = ? and email =?";
    ResultSet rs = null;
    MemberVO mv = null; // 회원정보를 저장하는 객체
    try {
        pstmt = conn.prepareStatement(sql);
        pstmt.setString(1, name);
        pstmt.setString(2, email);
        rs = pstmt.executeQuery();

        if(rs.next()) {
            mv = new MemberVO();
            mv.setMidx(rs.getInt("midx"));
            mv.setId(rs.getString("id"));
        }
    } catch (SQLException e) {
        e.printStackTrace();
    }finally {
        try {
            rs.close();
            pstmt.close();
            conn.close();
        } catch (SQLException e) {

            e.printStackTrace();
        }
    }

    return mv;
}

 

728x90
728x90

커뮤니티 사이트를 운영하려면 먼저 회원관리가 필요하겠죠?

때문에 회원가입을 할 수 있는 페이지를 먼저 만들어 보겠습니다.

그전에 앞서 sql을 사용할 수 있는 DB에 회원 테이블을 만들어줍니다. 

회원 테이블

저는 회원번호(midx), 아이디, 비밀번호, 이메일, 회원 가입일, 포인트, 삭제 여부(delyn), 관리자 권한을 회원 테이블 칼럼으로 설정했습니다.  

회원가입 페이지에서 기본적인 로직은 먼저 빈 input값이 없는지 확인합니다.

이후 비밀번호 두 값이 일치하는지 확인합니다. 아이디 중복확인의 경우 가입자 본인이 클릭해야 작동되는데

누르지 않고 생성버튼을 누르더라도 생성 버튼을 눌렀을 때 중복확인 기능이 먼저 작동해서

중복된 아이디가 DB에 있을 경우 회원가입 처리가 되지 않고 경고창을 띄웁니다.

 

<script type="text/javascript">
	// 두 비밀번호 입력값이 일치하는지 확인 후 데이터 전송
	function check() {
		if(document.fm.PWD.value != document.fm.PWDCheck.value){
			alert("비밀번호가 일치하지 않습니다.");
			document.fm.PWDCheck.focus();
			return;
		}
		document.fm.action = "<%=request.getContextPath()%>/member/memberJoinAction.do";
		document.fm.method = "post";
	}
	// DB에 중복 된 아이디가 있는지 확인하는 함수
    // if 절은 없어도 됨 input에 required를 적어놨기 때문에
	function checkedID(){
		if(document.fm.ID.value == ""){
			alert("아이디를 입력하세요.");
			document.fm.ID.focus();
			return;
		}
		
		document.fm.action = "<%=request.getContextPath()%>/member/idCheckAction.do";
		document.fm.method = "post";
		document.fm.submit();
	}
</script>

<!-- main content -->
    <section id="home">
      <h1 id="homeTitle"> 회 원 가 입 </h1>
	  <form name=fm>
	  <table >
        <tr>
          <td>아이디 :</td>
          <td>
			<input type="text" name="ID" required> 
			<button type="button" class="check" onclick="checkedID()"> 중복확인 </button>
		  </td>
        </tr>
		<tr>
          <td>이름 :</td>
          <td><input type="text" name="name" required></td>
        </tr>
        <tr>
          <td>비밀번호 :</td>
          <td><input type="password" name="PWD" required></td>
        </tr>
		<tr>
          <td>비밀번호 확인 :</td>
          <td><input type="password" name="PWDCheck" required></td>
        </tr>
        <tr>
          <td>이메일 :</td>
          <td><input type="email" name="email" required></td>
        </tr>
        <tr>	
          <td colspan="2" class="tdJoin">
            <button type="submit" id="join-btn" onclick="check()"><span>생성</span></button>
          </td>
        </tr>
      </table>
      </form>
    </section>

중복 확인을 누를경우 checkedID() 함수가 작동되고 멤버 컨트롤러  idCheckAction.do로 파라미터가 넘어옵니다.

//servlet MemberController로 id중복확인 
//DB에 회원가입하려는 ID와 같은 ID가 있는지 확인한다.
String ID = request.getParameter("ID");

MemberDAO md = new MemberDAO(); // 메서드 집합 class
boolean bl = md.checkedID(ID);	// 중복확인 메서드
PrintWriter out = response.getWriter();
if(bl) {
    out.println("<script>alert('중복된 아이디가 있습니다.');location.href=
    '"+request.getContextPath()+"/member/memberJoin.do'</script>");
}else{
    out.println("<script>alert('사용해도 되는 아이디입니다.');location.href=
    '"+request.getContextPath()+"/member/memberJoin.do'</script>");
}

// MemberDAO class의 id중복확인 메서드
// private Connection conn;
// private PreparedStatement pstmt; 미리 전역번수로 설정한 변수들
public boolean checkedID(String ID) {
    ResultSet rs = null;
    //입력받은 ID와 같은 ID가 DB에 있는지 조건절로 확인한다.
    String sql = "select id from member where id = ?";
    try {
        pstmt = conn.prepareStatement(sql);
        pstmt.setString(1, ID);
        rs = pstmt.executeQuery();
        if(rs.next()) {
            return true;
        }
    }catch(Exception e) {
        e.printStackTrace();
    }

    try {
        rs.close();
        pstmt.close();
    } catch (SQLException e) {
        e.printStackTrace();
    }
    return false;
}

생성 버튼을 눌렀을 경우 로직

//MemberController
//DB에 회원정보를 추가한다.
String ID = request.getParameter("ID");
String PWD = request.getParameter("PWD");
String name = request.getParameter("name");
String email = request.getParameter("email");

MemberDAO md = new MemberDAO();
boolean bl = md.checkedID(ID);  //id 중복확인을 누르지않고 회원가입을 하는 경우가 있으므로 
PrintWriter out = response.getWriter();
if(bl){
    out.println("<script>alert('중복된 아이디입니다.');location.href=
    '"+request.getContextPath()+"/member/memberJoin.do'</script>");
}else {
	// 중복된 아이디가 없는 경우 미리 생성된 DB테이블에 값을 넣는 메서드
    int value = md.insertMember(ID, PWD, name, email);
    if(value==1){
        response.sendRedirect(pj+"/main/index.do");
    }else{
       response.sendRedirect(pj+"/member/memberJoin.do");
    }
    
//MemberDAO class의 회원가입 메서드
//DB에 member로 테이블을 만들어 뒀다.
public int insertMember(String ID,String PWD,String name,String email){
    int value=0;		
    String sql="insert into member(midx,ID,PWD,name,email,point)"
            + "values(midx.nextval,?,?,?,?,?)";
    try{
    pstmt = conn.prepareStatement(sql);
    pstmt.setString(1, ID);
    pstmt.setString(2, PWD);
    pstmt.setString(3, name);
    pstmt.setString(4, email);
    pstmt.setInt(5, 0);
    value = pstmt.executeUpdate();

    }catch(Exception e){
        e.printStackTrace();
    }

    return value;
}

 

728x90
728x90

저번 시간에 자바 클래스까지 구상했으니 이번에는 초기화면을 만들어 보겠습니다.

우선 만들어본 초기화면을 보여드리고 간단하게 설명하겠습니다.

왼쪽 상단에 stocksophia를 클릭하면 index 페이지로 이동하게 설정해뒀습니다.

로그인 누르면 로그인으로 이동하고 옆에 아이콘을 클릭할 경우 이동할 수 있는 카테고리가 등장합니다. 

로그인한 경우와 아닌 경우 이동할 수 있는 카테고리가 다릅니다.

로그인 안했을 때
로그인 했을 때

해당 아이디는 관리자 권한까지 가지고 있어서 관리 페이지까지 보입니다.

로그인하지 않았을 경우 게시판의 글을 클릭할 수 없고 글쓰기 버튼이 보이지 않습니다.

게시판 글 자체는 가장 최근에 작성한 글 순서대로 DB에서 가져오고 검색과 페이징 기능을 추가했습니다.

페이지 자체는 반응형 웹사이트로 만들었는데 최대 사이즈는 1200px로 설정해뒀습니다.

 


저는 이 페이지를 만들면서 토글 버튼을 누르면 이동할 수 있는 카테고리를 보여주는 게 가장 어려웠습니다.

때문에 다른사람에게 혹시 도움이 될까 하여 코드를 남겨보겠습니다.

<!-- HTML -->
	<div class="dropdown">
        <button class="navbar__toggle-btn">
          <i class="fas fa-bars fa-2x"></i>
        </button>
        <div class="navbar__toggle_content" id="myDropdown">
		  <a href='#'>회원가입</a>
          <a href="#">자유게시판</a>
          <a href="#">추천게시판</a>
          <a href="#">공지사항</a>
        </div>
      </div>
      
 <script>
// 토글버튼 사용
//javascript
const navbarToggleBtn = document.querySelector('.navbar__toggle-btn');
navbarToggleBtn.addEventListener('click', () => {
	document.getElementById("myDropdown").classList.toggle('show');
});
 </script>
 
 <style>
 .navbar__toggle_content {
  display: none;
  position: absolute;
  right: 0;
  min-width: 10px;
  float: right;
  background-color: var(--color-light-green);
  box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
}

.show {
  display: block;
}
 </style>

처음에 css 스타일로 토글 컨텐츠에 display : none값을 주어 안 보이게 한 후에 

스크립트로 토글 버튼을 누를때 마다 토글 콘텐츠의 디스플레이 값을 변경하게 만들었습니다.

 

728x90
728x90

.each() 메서드

  • 매개변수로 입력한 함수를 사용한다.
  • for 반복문처럼 객체나 배열의 요소를 검사한다.
  • 기본 형태
<script>
	$.each(obj,function(index.item){});
    $(selector).each(function(index, item){});
    // index에는 배열의 인덱스가 오고 item에는 해당 인덱스의 값이 담긴다.
</script>
  • 예를 들어 여러개의 h1 태그가 있다면 각각의 값을 확인하고 사용할 수 있다.
<body>
	<h1> content1 </h1>
    <h1> content2 </h1>
    <h1> content3 </h1>
    <h1> content4 </h1>
    <h1> content5 </h1>
    <h1> content6 </h1>

<script>
	$("h1").each(function(idx,item){
    	//h1 각 태그에 적용할 함수 정의
    });
</script>
</body>

 

addClass() / removeClass() 메서드

  • addClass()는 문서 객체에 class 속성을 추가하는 메서드
  • removeClass()는 문서 객체에 class 속성을 제거하는 메서드
  • 예제 
  • class 버튼을 누르면 div1 class를 추가해 css가 적용되고 remove 버튼을 누르면 class가 삭제되어 css적용이 풀린다.
<head>
<style type="text/css">
	.div1 {
		display: inline-block;
		margin: 10px;
		background-color: yellow;
		border:1px solid black;
	}
</style>
</head>
<body>
	<div >div1</div>
	<div >div1</div>
	<button onclick="clickbtn()"> class </button>
	<button onclick="removeBtn()"> remove</button>
	
<script>
	function clickbtn(){
		$("div").addClass("div1");
	} 
	function removeBtn(){
		$("div").removeClass("div1");
	}
</script>
</body>

 

attr() / removeAttr() 메서드

  • 특정 속성의 값을 알아내거나 추가/ 제거할 수 있다.
<body>
	<input type="text" id="text1">

<script>
	//input의 타입의 값을 반환한다.
	$("input").attr("type");
    
    //input의 타입 속성의 값을 password로 변경/추가한다.
    $("input").attr("type","password");
    
    //input 태그에 있는 id 속성을 제거한다.
    $("input").removeAttr("id");
<script>
</body>

 

728x90

+ Recent posts