일시적으로 사용된다는 것은 재사용이 되지 않거나 그럴 필요가 없다는 것인데 이건 확장성이 좋지 않다는 걸 의미합니다.
OOP 측면에서는 확장성 있게 만드는 걸 지향하지만 오히려 설계를 해치는 경우가 있습니다.
이러한 경우에 익명 클래스를 많이 씁니다.
1. 프로그램 한번만 사용되어야하는 객체의 경우 [ UI 이벤트 처리, 스레드 객체 등... ]
밑에는 UI 이벤트 처리에 대한 예제이다 사실상 어거지로 만든 거고 클래스로도 처리가 가능할 거 같은데 설계 부분에 솔직히 이해는 가지만 크게 와닿진 않는다
Button를 생성해서 인터페이스를 즉각 구현해서 익명 객체(매개변수)를 구현한 예제이다.
RingListener
package org.example;
public interface RingListener {
public void ring ();
}
Button
package org.example;
public class Button {
public void ring(RingListener listener){
listener.ring();
}
}
Main
package org.example;
public class Main {
public static void main(String[] args) {
Button button = new Button();
button.ring(new RingListener() {
@Override
public void ring() {
System.out.println("1회용 ring ring");
}
}); // 1회용 ring ring
}
}
2. 비즈니스 로직이 제각각이며 재사용성이 없어서 매번 클래스를 생성해야하는 경우 [ 아래와 예제와 같이 버튼이 여러개인데 각각 다른 역할의 응답을 하는 경우 버튼이 많아질수록 기하급수적으로 늘어난다]
Button
package org.example;
public class Button {
void turnOn () {
System.out.println("버튼을 킵니다.");
}
}
Anonymous
package org.example;
public class Anonymous {
/** 객체 생성해 재정의하기 **/
Button button = new Button(){
String name = "방 스위치";
// 익명 클래스 안에서만 살아있기 때문에 사용 불가
void turnOff(){
System.out.println("Down 버튼을 눌러 " + name + "를 끕니다");
}
// 재정의를 했기 때문에 사용 가능
@Override
void turnOn(){
System.out.println("Up 버튼을 눌러 " + name + "를 킵니다");
}
};
/** 클래스안 메소드에서 객체 생성해 재정의하기 [1번과 동일하다] **/
void method1(){
Button button1 = new Button(){
String name = "정답 스위치";
// 익명 클래스 안에서만 살아있기 때문에 사용 불가
void turnOff(){
System.out.println("X 버튼을 눌러 " + name + "를 끕니다");
}
// 재정의를 했기 때문에 사용 가능
@Override
void turnOn(){
System.out.println("O 버튼을 눌러 " + name + "를 킵니다");
}
};
button1.turnOn();
}
/** 매개변수에 익명함수 사용해보기 **/
void method2(Button button){
button.turnOn();
}
}
Main Thread
public class Main {
public static void main(String[] args) {
Anonymous button = new Anonymous();
// 1번 방법으로 호출
button.button.turnOn(); // 위 버튼을 눌러 방 스위치를 킵니다
// 2번 방법으로 호출
button.method1(); // O 버튼을 눌러 정답 스위치를 킵니다
// 3번 방법으로 호출
// 2번은 Anonymous에서 상세 내용을 작성한 거고 3번 방법은 그걸 밖에서 내가 정의해서 사용하는 차이가 있다.
// 즉, 어디서 상세 내용을 재정의하냐에 따라 다름
button.method2(new Button(){
String name = "엘레베이터";
// 익명 클래스 안에서만 살아있기 때문에 사용 불가
void turnOff(){
System.out.println("엑스 버튼을 눌러 " + name + "를 닫습니다.");
}
// 재정의를 했기 때문에 사용 가능
@Override
void turnOn(){
System.out.println("동그라미 버튼을 눌러 " + name + "를 엽니다.");
}
}); // 동그라미 버튼을 눌러 엘레베이터를 엽니다.
}
}
📝함수형 인터페이스
함수형 인터페이스란 1 개의 추상 메소드를 갖는 인터페이스로 여러 개의 디폴트 메서드가 있더라도 추상 메서드가 오직 하나면 함수형 인터페이스라고 한다 (default method 또는 static method 는 여러 개 존재해도 상관 없다)
매번 함수형 인터페이스를 직접 만들어서 사용하는 건 번거로운 일로서 그래서 Java 에서는 기본적으로 많이 사용되는 함수형 인터페이스를 제공한다 기본적으로 제공되는 것만 사용해도 충분하기 때문에 직접 만드는 경우는 거의 없다.
예제 코드
@FunctionalInterface // 함수형 인터페이스 조건에 맞는지 검사
interface CustomInterface<T> {
T myCall(); // abstract method 오직 하나!!
// default method 는 존재해도 상관없음
default void printDefault() {
System.out.println("Hello Default");
}
// static method 는 존재해도 상관없음
static void printStatic() {
System.out.println("Hello Static");
}
}
함수형 인터페이스 장점
같은 Input에 같은 Ouput을 제공하고 변경 개념이 아니라 복사되고 복사 된 것을 함수를 거쳐 결과를 내기 때문에 멀티스레드 공유자원에 안전하다
코드를 간결하고 가독성 있게 작성할 수 있다.
함수형 인터페이스 단점
익명 함수의 재사용이 불가능
재귀함수의 구현이 어렵다.
재사용이 불가능하므로 비슷한 함수 구현이 잦을 수 있다.
디버깅이 어렵다.
Java에서 제공하는 많이 쓰는 함수형 인터페이스
Runnable → 파라미터 없고 리턴값도 없는 경우 / 추상메소드 = run()
Consumer<T> → 파라미터 있고 리턴값 없는 경우 / 추상메소드 = accept(T t)
Supplier<T> → 파라미터 없고 리턴값 있는 경우 / 추상메소드 = get()
Function<T,R>→ 파라미터 있고 리턴값 있는 경우 / 추상메소드 = apply(T t) // T파라미터, R 반환값
Operator<T> → 파라미터 있고 리턴값 있는 경우 / 연산용도, 임의의 타입 전달하고 임의의 타입 반환
Predicate<T> → 파라미터 있고 리턴값(boolean) 있는 경우 / 추상 메소드 = test(T t)
Runnable
public class Main {
public static void main(String[] args) {
Runnable runnable = () -> System.out.println("Runnable run");
runnable.run(); // Runnable run
}
}
Consumer
public class Main {
public static void main(String[] args) {
Consumer<String> con = text -> System.out.println("parameter : " + text);
con.accept("Consumer Testing!!");
/** Consumer Chaining **/
Consumer<String> cutFruit = fruit -> System.out.println(fruit + "를 깍습니다." ); // Apple를 깍습니다.
Consumer<String> eatFruit = fruit -> System.out.println(fruit + "를 먹습니다."); // Apple를 먹습니다.
cutFruit.andThen(eatFruit).accept("Apple");
}
}
Consumer 파생형
BiConsumer => 파라미터를 2개 받아서 처리
DoubleConsumer ==> double타입으로 파라미터를 받아서 처리
IntConsumer ==> int타입으로 파라미터를 받아서 처리
LongConsumer ==> long타입으로 파라미터를 받아서 처리
ObjDoubleConsumer ==> 두개의 파라미터중에 하나는 obj 다른하나는 double
ObjIntConsumer ==> 두개의 파라미터중에 하나는 obj 다른하나는 int
ObjLongConsumer ==> 두개의 파라미터중에 하나는 obj 다른하나는 long
public class Main {
public static void main(String[] args) {
BiConsumer<String, Integer> bi = (text, num) -> System.out.println("파리미터 2개 -> " + text + " + " + num);
bi.accept("프로그래밍!!", 1234); // 파리미터 2개 -> 프로그래밍!! + 1234
}
}
Supplier
public class Main {
public static void main(String[] args) {
Supplier<String> supplier = () -> "supplier has return but no params";
System.out.println(supplier.get()); // supplier has return but no params
}
}
Supplier 파생형
BooleanSupplier : boolean 타입으로 반환
DoubleSupplier : double 타입으로 반환
IntSupplier : int 타입으로 반환
LongSupplier : long 타입으로 반환
Operator
public class Main {
public static void main(String[] args) {
BinaryOperator<Integer> binaryOperator = (num1, num2) -> num1 + num2;
int result = binaryOperator.apply(5, 10);
System.out.println(result); // 15
}
}
Operator라는 인터페이스는 없다
BinaryOperator<T> : BiFunction<T,U,R> 상속 → 두개의 파라미터 전달하고 반환 ( 모두 데이터타입 동일 )
UnaryOperator<T> : Function<T,R> 상속 → 하나의 파라미터 전달하고 반환 ( 모두 데이터타입 동일 )
Operator 파생형
DoubleBinaryOperator : 두개의 파라미터 double 전달하고 double 반환 ( 모두 데이터타입 동일 )
DoubleUnaryOperator : 하나의 파라미터 double 전달하고 double반환
IntBinaryOperator : 두개의 파라미터 int 전달하고 int 반환 ( 모두 데이터타입 동일 )
IntUnaryOperator : 하나의 파라미터 int 전달하고 int반환
LongBinaryOperator : 두개의 파라미터 long 전달하고 long 반환 ( 모두 데이터타입 동일 )
LongUnaryOperator : 하나의 파라미터 long 전달하고 long반환
Predicate
package org.example;
import java.util.function.*;
public class Main {
public static void main(String[] args) {
Predicate<String> isNameString = str -> str.equals("name");
System.out.println("is String Name? test() -> " + isNameString.test("age"));
// is String Name? test() -> false
System.out.println("is String Name? test() -> " + isNameString.test("name"));
// is String Name? test() -> true
}
}
📝 람다식 [익명 객체]
람다는 익명 객체이다 (익명 함수는 함수가 독자적으로 존재해야하지만 Java는 클래스에 종속적이기 때문에 익명 객체라는 말이 맞다)
람다식 축약 예제 따라해보기
class Calculator {
int sum(int x, int y){
return x + y;
} // 람다식 : (x,y) -> x+y;
void print(String name, int i){
System.out.println(name + " = " + i);
} // 람다식 : (name,i) -> System.out.println(name + " = " + i);
int square(int x){
return x * x;
} // 람다식 : x -> x * x
int roll() {
return (int) (Math.random() * 6);
} // 람다식 : () -> (int) (Math.random() * 6);
}
함수반환 타입을 생략할 수 있다.
함수명을 생략할 수 있다.
인자 타입을 생략할 수 있다.
함수내용인 중괄호 내용이 한줄이면 중괄호는 생략이 가능하다
함수형 인터페이스 + 람다식 이용해보기
package org.example;
public class Main {
public static void main(String[] args) {
/** 익명 객체 잘못된 사용 방법 **/
Object object = new Object() {
int max(int a, int b){
return a > b ? a : b;
}
}; // Object에 max라는게 없기 때문에 object.max(3,5); 사용 불가
/** 함수형 인터페이스 **/
// MyFunction myFunction = new MyFunction() {
// @Override
// public int max(int a, int b) {
// return a > b ? a: b;
// }
// };
/** 함수형 인터페이스 + 람다식 **/
MyFunction myFunction = (a, b) -> a > b ? a : b;
// 함수형 인터페이스 자체가 추상 메서드가 하나밖에 없기 때문에
// 매개변수 및 리턴값에 뭐가 나올지 알고 있어서 생략이 가능하다
System.out.println(myFunction.max(3,5)); // 5
}
interface MyFunction {
public abstract int max(int a, int b);
}
}
@Getter // Getter 생성
@Setter // Setter 생성
@RequiredArgsConstructor // 생성자 DI
@Data // Getter + Setter 생성
@AllArgsConstructor // 생성자 생성
@NoArgsConstructor // 기본생성자
@ToString(of = {"id","password"})
public class Account {
// private final String id; // DB에서 값을 받아와 수정할 일이 없을 때는 final 로 쓰는게 trend
// private final int password; // DB에서 값을 받아와 수정할 일이 없을 때는 final 로 쓰는게 trend
private String id;
private int password;
@Builder // 내가 원하는 생성자 파라미터 지정 가능
public Account(String id, int password) {
this.id = id;
this.password = password;
}
}
public class ApiExceptionV2Controller {
...
@ResponseStatus(HttpStatus.BAD_REQUEST) // 해당 Status 설정 없으면 200(정상)이 나가기 때문에 따로 설정 필요
@ExceptionHandler(IllegalArgumentException.class) // 해당 컨트롤러에서만 IllegalArgumentException 발생 시 작동
public ErrorResult illegalExHandler(IllegalArgumentException e){
log.error("[exception] ex",e);
return new ErrorResult("BAD",e.getMessage());
/**
* {
* "code": "BAD",
* "message": "잘못된 입력 값"
* }
*/
}
@GetMapping("/api2/members/{id}")
public MemberDto getMember(@PathVariable("id") String id) {
if (id.equals("ex")) {
throw new RuntimeException("잘못된 사용자");
}
if (id.equals("bad")) {
throw new IllegalArgumentException("잘못된 입력 값");
}
if (id.equals("user-ex")) {
throw new UserException("사용자 오류");
}
return new MemberDto(id, "hello " + id);
}
}
현 컨트롤러 내에서 지정한 에러 발생시 핸들링할 수 있는 역할을 한다
📝@Transactional
@Transactional
public void updateSomething(MembmerDto membmerDto){
// ... 업데이트
// 실패!! -> RollBack
}
📝@TestConfiguration
@TestConfiguration
static class TestConfig {
private final DataSource dataSource;
public TestConfig(DataSource dataSource) {
this.dataSource = dataSource;
}
@Bean
MemberRepositoryV3 memberRepositoryV3() {
return new MemberRepositoryV3(dataSource);
}
@Bean
MemberServiceV3_3 memberServiceV3_3() {
return new MemberServiceV3_3(memberRepositoryV3());
}
}
@RestController
@RequestMapping("/mapping/users")
public class MappingClassController {
/**
* GET /mapping/users
*/
@GetMapping
public String users() {
return "get users";
}
/**
* POST /mapping/users
*/
@PostMapping
public String addUser() {
return "post user";
}
/**
* GET /mapping/users/{userId}
*/
@GetMapping("/{userId}")
public String findUser(@PathVariable String userId) {
return "get userId=" + userId;
}
/**
* PATCH /mapping/users/{userId}
*/
@PatchMapping("/{userId}")
public String updateUser(@PathVariable String userId) {
return "update userId=" + userId;
}
}
일반적으로 공통적인 URL을 클래스 위에 선언해 사용한다
@RequestMapping(value = "/modelAndView.do" method = RequestMethod.GET)
public ModelAndView returnModelAndView(HttpServletResponse response,
HttpServletRequest request) {
ModelAndView modelAndView = new ModelAndView();
// WEB-INF/views/가 기본 경로
modelAndView.setViewName("request"); // request.jsp 페이지 노출 WEB-INF/views/request.jsp
modelAndView.addObject("name","James"); // index.jsp에 name : James 값을 전달
return modelAndView;
}
리턴 종류 - ModelAndView
노출시킬 jsp 페이지와 값을 보낼 수 있습니다.
@RequestMapping(value = "/string.do")
public String returnString(HttpServletResponse response,
HttpServletRequest request) {
return "request"; // request.jsp 페이지 노출
}
@RequestMapping(value = "/model.do", method = RequestMethod.GET)
public void body4(@ModelAttribute BodyVO bodyVO,
HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException {
// 호출 URI : model.do?name=lee&age=20
// @ModelAttribute 사용시에는 해당 클래스의 객체를 생성하고 값을 set 해준다.
// (default로 @ModelAttribute 안 적어도 @ModelAttribute 적은 거 처럼 동작)
// 객체를 받는 경우는 무조건 쿼리스트링으로만 동작함 (body 인식 못 함)
System.out.println(">>> " + bodyVO.getName()); // >>> lee
System.out.println(">>> " + bodyVO.getAge()); // >>> 20
}
사용시 해당 클래스의 객체를 생성하고 값을 set 해준다.(default로 @ModelAttribute 안 적어도 @ModelAttribute 적은 거 처럼 동작)
객체를 받는 경우는 무조건 쿼리스트링으로만 동작함 (body 내용은 자동 DI 못 함)
📝 @ResponseBody,@RequestBody
@RequestMapping(value = "/urlencoded.do", method = RequestMethod.POST)
public void urlencoded(String code,String name) {
// 호출 Url : urlencoded.do?name=Lee
// Content-Type : application/x-www-form-urlencoded
// Body : key → code, Value → ABC
System.out.println(">>> " + code); // ABC
System.out.println(">>> " + name); // Lee
// 일반적으로 POST 방식에 쿼리스트링을 붙히지 않지만 붙혀도 인식이 가능함
// x-www-form-urlencoded 의 경우 쿼리스트링처럼 code=ABC 인식을 한다.
}
/** application/x-www-form-urlencoded **/
@RequestMapping(value = "/urlencodedToObject.do", method = RequestMethod.POST)
public void urlencodedToObject(BodyVO bodyVO) {
// 호출 Url : urlencoded.do?name=Lee
// Content-Type : application/x-www-form-urlencoded
// Body : key → code, Value → ABC
System.out.println(">>> " + bodyVO.getName()); // ABC
System.out.println(">>> " + bodyVO.getAge()); // ABC
}
/** application/x-www-form-urlencoded의 GET의 경우 따로 RequestBody가 필요없다. **/
/** text **/
@RequestMapping(value = "/text.do", method = RequestMethod.POST)
public void text(@RequestBody String bodyContents) {
// 호출 Url : text.do
// body : this is text
// Content-Type : text 또는 text/html
System.out.println(">>> " + bodyContents); // this is text
}
/** application/json **/
@RequestMapping(value = "/json.do", method = RequestMethod.POST)
public void json(@RequestBody String bodyContents) {
// 호출 Url : json.do
// body : {"content" : "this is json"}
// Content-Type : application/json
System.out.println(">>> " + bodyContents); // {"content" : "this is json"}
}
/** application/json (Object Injection) **/
@RequestMapping(value = "/jsonToObject.do", method = RequestMethod.POST)
public @ResponseBody HashMap<String, Object> jsonToObject(@RequestBody BodyVO bodyVO) {
/** BodyVO (HashMap도 또한 Object이기 떄문에 동일하게 적용 된다. **/
// String name
// String age → int 형으로 값을 보내면 못 자동 Injection이 안 된다. "age" : 20 (X)
/** 참고로 BodyVO에 default값이 설정되어 있어도 ReuqestBody에 넘어온 값이 없다면(body에 json을 안 보내면) null로 덮어씌워진다. **/
/** 필요사항에서는 데이터를 까서 검증하는 작업을 직접해야할 거 같다. **/
// 호출 Url : jsonToObject.do
// body : {"name" : "lee", "age":"20"}
// Content-Type : application/json
System.out.println(">>> " + bodyVO.getName()); // lee
System.out.println(">>> " + bodyVO.getAge()); // 20
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("bodyVO", bodyVO);
return map;
}
/** responseBody란 처리한 결과를 클라이언트에게 다시 돌려줘서 읽을 수 있게한다. (즉 return 값이 있어야한다.) **/
// 미사용시 → 404 페이지를 찾을 수 없음
// 사용시 → {"bodyVO" : {"name" : "lee", "age":"20"}}
/**
* jsonToObject.do을 사용하기 위해서는 하기 라이브러리가 필요 (body 내용을 Object에 Injeciton)
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.3</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
**/
package com.lsj.study.controller;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.lsj.study.util.MosaicCaptcha;
@Controller
public class CaptchaController {
/** ------------ Mosaic CaptCha Page ------------ **/
@GetMapping(value = "/captcha/mosaic")
public String captchaMosaic(HttpServletRequest reques) {
/** Referer 필요시 사용 **/
// String referer = request.getHeader("Referer");
//
// if (referer.equals(rightReferer)) return "captcha/captcha_mosaic";
// else return "captcha/wrong_page";
return "captcha/captcha_mosaic";
}
/** ------------ Mosaic CaptCha Multiple Choice (객관식 그림) ------------ **/
@GetMapping(value = "/captcha/mosaic/multiple/choice")
public void getQuestionList(HttpServletResponse response, HttpSession session) throws IOException {
MosaicCaptcha mosaicCaptcha = new MosaicCaptcha();
/** 객관식 번호 가져오기 **/
BufferedImage choiceNum1 = mosaicCaptcha.getMosaicQuestionImage(session);
BufferedImage choiceNum2 = mosaicCaptcha.getMosaicQuestionImage(session);
BufferedImage choiceNum3 = mosaicCaptcha.getMosaicQuestionImage(session);
BufferedImage choiceNum4 = mosaicCaptcha.getMosaicAnswerImage(session);
/** 화면 준비 **/
OutputStream out = response.getOutputStream();
/** 객관식 정보 담기 **/
Map<String, BufferedImage> multipleChoiceMap = new HashMap<String, BufferedImage>();
multipleChoiceMap.put("wrong1", choiceNum1);
multipleChoiceMap.put("wrong2", choiceNum2);
multipleChoiceMap.put("wrong3", choiceNum3);
multipleChoiceMap.put("answer", choiceNum4);
/** 객관식 번호 섞기 **/
List<String> keys = new ArrayList(multipleChoiceMap.keySet());
Collections.shuffle(keys);
/** 객관식 번호 부여하기 **/
BufferedImage questionNum1 = mosaicCaptcha.drawNumberOnMultipleChoiceImage("1");
BufferedImage questionNum2 = mosaicCaptcha.drawNumberOnMultipleChoiceImage("2");
BufferedImage questionNum3 = mosaicCaptcha.drawNumberOnMultipleChoiceImage("3");
BufferedImage questionNum4 = mosaicCaptcha.drawNumberOnMultipleChoiceImage("4");
/** key로 정답을 찾아 session 영역에 저장 **/
int answerNum = keys.indexOf("answer");
session.setAttribute("mosaicAnswer", answerNum + 1);
System.out.println("Mosaic-answer : " + (answerNum + 1));
/** 객관식 리스트 합쳐 하나의 이미지로 재구성 **/
try {
/** 기본 바탕화면 구성 **/
BufferedImage mergedImage = new BufferedImage(550, 100, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = (Graphics2D) mergedImage.getGraphics();
graphics.setPaint(new Color(255, 255, 255));
graphics.fillRect(0, 0, mergedImage.getWidth(), mergedImage.getHeight());
/** 객관식 리스트 **/
graphics.drawImage(multipleChoiceMap.get(keys.get(0)), 0, 0, null);
graphics.drawImage(multipleChoiceMap.get(keys.get(1)), 150, 0, null);
graphics.drawImage(multipleChoiceMap.get(keys.get(2)), 300, 0, null);
graphics.drawImage(multipleChoiceMap.get(keys.get(3)), 450, 0, null);
/** 객관식 리스트의 번호 부여 **/
graphics.drawImage(questionNum1, 10, 0, null);
graphics.drawImage(questionNum2, 160, 0, null);
graphics.drawImage(questionNum3, 310, 0, null);
graphics.drawImage(questionNum4, 460, 0, null);
/** 화면에 출력 **/
response.setContentType("image/png");
ImageIO.write(mergedImage, "png", out);
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
/** ------------ Mosaic CaptCha Question (문제) ------------ **/
@GetMapping(value = "/captcha/mosaic/question")
public void getMosaicQuestion(HttpServletResponse response, HttpSession session)
throws IOException {
MosaicCaptcha mosaic = new MosaicCaptcha();
/** 랜덤 이미지 받기 **/
String fileImagePath = mosaic.getRandomMosaicImage(session);
/** 자를 부분 랜덤 XY 좌표 설정 **/
mosaic.setCutXYPoint(session);
File file = new File(fileImagePath); // 리사이즈할 파일 정보
int resizedWidth = 500; // 리사이즈할 가로 길이
int resizedHeight = 500; // 리사이즈할 세로 길이
/** 리사이징 **/
BufferedImage blackBoxImage = mosaic.resize(file, resizedWidth, resizedHeight);
/** 문제 낼 곳에 블랙박스 생성 **/
int blackBoxWidth = 100;
int blackBoxHeight = 100;
mosaic.drawBlackBox(blackBoxImage, blackBoxWidth, blackBoxHeight, session);
/** 화면에 뿌릴 준비 **/
OutputStream out = response.getOutputStream();
/** 화면에 뿌리기 **/
response.setContentType("image/png");
ImageIO.write(blackBoxImage, "png", out);
}
/** ------------ Mosaic CaptCha Confirm (정답 확인) ------------ **/
@GetMapping(value = "/captcha/mosaic/answer")
public @ResponseBody Map<Object, Object> confirmCaptChaMosaic(HttpServletRequest request, HttpSession session) {
Map<Object, Object> result = new HashMap<>();
/** 정답 가져오기 **/
session = request.getSession();
String answer = String.valueOf((int) session.getAttribute("mosaicAnswer")); // 정답
String userAnswer = request.getParameter("userAnswer"); // 유저 정답
System.out.println("Mosaic-userAnswer : " + userAnswer);
/** 정답 확인 **/
if (userAnswer.equals(answer)) result.put("response", "right");
else result.put("response", "wrong");
return result;
}
}
Controller
package com.lsj.study.util;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpSession;
import org.springframework.core.io.ClassPathResource;
import com.lsj.study.vo.ImageVo;
public class MosaicCaptcha {
/**
* @param session : 사용자 session
* @return 랜덤 모자이크에 사용할 이미지 가져오기
*/
public String getRandomMosaicImage(HttpSession session) {
int randomImage = (int) (Math.random() * 6) + 1;
String filePath = "";
switch (randomImage) {
case 1:
filePath = new ClassPathResource("image/captcha/mosaic/mosaic_1.png").getPath();
break;
case 2:
filePath = new ClassPathResource("image/captcha/mosaic/mosaic_2.png").getPath();
break;
case 3:
filePath = new ClassPathResource("image/captcha/mosaic/mosaic_3.png").getPath();
break;
case 4:
filePath = new ClassPathResource("image/captcha/mosaic/mosaic_4.png").getPath();
break;
case 5:
filePath = new ClassPathResource("image/captcha/mosaic/mosaic_5.png").getPath();
break;
case 6:
filePath = new ClassPathResource("image/captcha/mosaic/mosaic_6.png").getPath();
break;
}
URL resource = getClass().getClassLoader().getResource(filePath);
filePath = resource.getFile();
session.setAttribute("questionImagePath", filePath);
session.setAttribute("imageNumber", randomImage);
System.out.println("QuestionfilePath : " + filePath);
return filePath;
}
/**
* @param session : 사용자 session
* @return 잘못된 모자이크 이미지 만들기
*/
public String getWrongRandomMosaicImage(HttpSession session) {
int questionImage = 1;
if(session.getAttribute("imageNumber") != null)
questionImage = (int) session.getAttribute("imageNumber");
int randomImage = (int) (Math.random() * 6) + 1;
while (true) {
if (questionImage != randomImage)
break;
else
randomImage = (int) (Math.random() * 6) + 1;
}
String filePath = "";
switch (randomImage) {
case 1:
filePath = new ClassPathResource("image/captcha/mosaic/mosaic_1.png").getPath();
break;
case 2:
filePath = new ClassPathResource("image/captcha/mosaic/mosaic_2.png").getPath();
break;
case 3:
filePath = new ClassPathResource("image/captcha/mosaic/mosaic_3.png").getPath();
break;
case 4:
filePath = new ClassPathResource("image/captcha/mosaic/mosaic_4.png").getPath();
break;
case 5:
filePath = new ClassPathResource("image/captcha/mosaic/mosaic_5.png").getPath();
break;
case 6:
filePath = new ClassPathResource("image/captcha/mosaic/mosaic_6.png").getPath();
break;
}
URL resource = getClass().getClassLoader().getResource(filePath);
filePath = resource.getFile();
System.out.println("wrongFilePath : " + filePath);
return filePath;
}
/**
* @param file : 리사이즈할 파일
* @param width : 리사이즈할 넓이
* @param height : 리사이즈할 높이
* @return 리사이즈된 이미지
* @throws IOException
*/
public BufferedImage resize(File file, int width, int height) throws IOException {
/** 이미지 가져오기 **/
InputStream inputStream = new FileInputStream(file);
/** 받은 이미지 읽기 **/
BufferedImage inputImage = ImageIO.read(inputStream);
/** 문제 이미지의 자를 부분의 크기와 위치 범위가 정해져있기 때문에 동일한 크기로 리사이징 **/
BufferedImage outputImage = new BufferedImage(width, height, inputImage.getType());
Graphics2D graphics2D = outputImage.createGraphics();
graphics2D.drawImage(inputImage, 0, 0, width, height, null); // 그리기
graphics2D.dispose(); // 자원해제
return outputImage;
}
/**
* @param session : 사용자 session
* @why 모자이크로 만들 자를 이미지 X, Y 좌표를 설정
*/
public void setCutXYPoint(HttpSession session) {
int cutXPoint;
int cutYPoint;
/** session이 안 잡힌 경우 예외처리 **/
if (session.getAttribute("XPoint") == null || session.getAttribute("YPoint") == null) {
cutXPoint = (int) (Math.random() * 400);
cutYPoint = (int) (Math.random() * 400);
session.setAttribute("XPoint", cutXPoint);
session.setAttribute("YPoint", cutYPoint);
/** session 영역이 존재하는 경우 **/
} else {
cutXPoint = (int) (Math.random() * 400);
cutYPoint = (int) (Math.random() * 400);
session.setAttribute("XPoint", cutXPoint);
session.setAttribute("YPoint", cutYPoint);
}
}
/**
* @param blackBoxImage : 블랙박스 이미지
* @param blackBoxWidth : 블랙박스 넓이
* @param blackBoxHeight : 블랙박스 높이
* @param session : 사용자 session
* @why 자른 모자이크를 검은색 사각형 박스로 덮어씌우기
*/
public void drawBlackBox(BufferedImage blackBoxImage, int blackBoxWidth, int blackBoxHeight, HttpSession session) {
int cutXPoint = (int) session.getAttribute("XPoint"); // 왼쪽 상단 X좌표
int cutYPoint = (int) session.getAttribute("YPoint"); // 왼쪽 상단 Y좌표
/** 블랙박스 그리기 **/
Graphics graphics = blackBoxImage.getGraphics();
graphics.setColor(Color.BLACK);
graphics.fillRect(cutXPoint, cutYPoint, blackBoxWidth, blackBoxHeight); // 채우기 사각형
// graphics.drawRect(cutXPoint, cutYPoint, blackBoxWidth, blackBoxHeight); // 테두리 사각형
graphics.dispose();
}
/**
*
* @param numberToDraw : 자른 객곽신 이미지 위에 그릴 숫자
* @return 자른 객관식 이미지 위에 객관식 번호 기입한 이미지
*/
public BufferedImage drawNumberOnMultipleChoiceImage(String numberToDraw) {
/** 이미지화할 글자 설정 **/
int imageWidth = 550; // 문제 이미지 넓이
int imageHeight = 100; // 문제 이미지 높이
Color color = new Color(255, 0, 0); // 글자 색
Font font = new Font("TimeRoman", Font.PLAIN, 35); // 이미지 글체
ImageVo stringImage = new ImageVo(imageWidth, imageHeight, color, font);
/** 문자를 그릴 종이 만들기 **/
BufferedImage image = getWhitePaper(stringImage);
/** 문제 받아오기 **/
String question = numberToDraw;
/** 글자 이미지가 생성될 위치 선정 **/
Graphics2D graphics2 = image.createGraphics(); // Graphics2D 와 BufferedImage는 연동 된 느낌?
/** 글자색과 글자체 받아오기 **/
graphics2.setColor(stringImage.getImageColor());
graphics2.setFont(stringImage.getFont());
/** 해당 내용으로 그리기 작업 **/
graphics2.drawString(question, 0, 40);
graphics2.dispose();
return image;
}
/**
* @param session : 사용자 session
* @return 문제에서 자를 이미지 가져오기
*/
public BufferedImage getMosaicQuestionImage(HttpSession session) throws IOException {
MosaicCaptcha mosaic = new MosaicCaptcha();
/** 랜덤 이미지 받기 **/
String fileImagePath = mosaic.getWrongRandomMosaicImage(session);
/** 리사이즈할 파일 정보 받기 **/
File file = new File(fileImagePath);
int resizedWidth = 500; // 리사이즈할 가로 길이
int resizedHeight = 500; // 리사이즈할 세로 길이
/** 리사이징 **/
BufferedImage resizedImage = mosaic.resize(file, resizedWidth, resizedHeight);
/** 문제 이미지의 자를 부분의 크기와 위치 설정 **/
int cutWidth = 100; // 자를 넓이
int cutHeight = 100; // 자를 높이
int cutXPoint = (int) (Math.random() * 400); // 랜덤 X포인트 (자를 왼쪽 상단 위치)
int cutYPoint = (int) (Math.random() * 400); // 랜덤 Y포인트 (자를 왼쪽 상단 위치)
BufferedImage cutImage = resizedImage.getSubimage(cutXPoint, cutYPoint, cutWidth, cutHeight); // 잘린 이미지
return cutImage;
}
/**
* @param session : 사용자 session
* @return 잘린 정답 이미지
*/
public BufferedImage getMosaicAnswerImage(HttpSession session) throws IOException {
MosaicCaptcha mosaic = new MosaicCaptcha();
/** 문제 이미지 경로 **/
String fileImagePath = (String) session.getAttribute("questionImagePath");
File file = new File(fileImagePath); // 리사이즈할 파일 정보 받기
int resizedWidth = 500; // 리사이즈할 가로 길이
int resizedHeight = 500; // 리사이즈할 세로 길이
/** 리사이징 **/
BufferedImage resizedImage = mosaic.resize(file, resizedWidth, resizedHeight);
/** 문제 이미지의 자를 부분의 크기와 위치 설정 **/
int cutWidth = 100; // 자를 넓이
int cutHeight = 100; // 자를 높이
int cutXPoint = (int) session.getAttribute("XPoint"); // 왼쪽 상단 X좌표
int cutYPoint = (int) session.getAttribute("YPoint"); // 왼쪽 상단 Y좌표
/** 잘린 이미지 **/
BufferedImage cutImage = resizedImage.getSubimage(cutXPoint, cutYPoint, cutWidth, cutHeight);
return cutImage;
}
/**
* @param customImage : 그림을 그릴 종이 설정
* @return 그림을 그릴 종이
*/
public BufferedImage getWhitePaper(ImageVo customImage) {
// 글자를 그릴 종이 크기 설정
int width = customImage.getImageWidth();
int height = customImage.getImageHeight();
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
return image;
}
}
Util
package com.lsj.study.vo;
import java.awt.Color;
import java.awt.Font;
public class ImageVo {
private int imageWidth;
private int imageHeight;
private Color imageColor;
private Font font;
public ImageVo(int imageWidth, int imageHeight, Color imageColor, Font font) {
this.imageWidth = imageWidth;
this.imageHeight = imageHeight;
this.imageColor = imageColor;
this.font = font;
}
public int getImageWidth() {
return imageWidth;
}
public void setImageWidth(int imageWidth) {
this.imageWidth = imageWidth;
}
public int getImageHeight() {
return imageHeight;
}
public void setImageHeight(int imageHeight) {
this.imageHeight = imageHeight;
}
public Color getImageColor() {
return imageColor;
}
public void setImageColor(Color imageColor) {
this.imageColor = imageColor;
}
public Font getFont() {
return font;
}
public void setFont(Font font) {
this.font = font;
}
}
package com.lsj.study.controller;
import java.awt.Color;
import java.awt.Font;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.lsj.study.Util.NumberCaptcha;
import com.lsj.study.Vo.ImageVo;
@Controller
public class CaptchaController {
/** ------------ Number CaptCha Page ------------ **/
@GetMapping(value = "/captcha/number")
public String captchaNumber(HttpServletRequest request) {
/** Referer 필요시 사용 **/
// String referer = request.getHeader("Referer");
//
// if (referer.equals(rightReferer)) return "captcha/captcha_number";
// else return "captcha/wrong_page";
return "captcha/captcha_number";
}
/** ------------ Number CaptCha Question (문제)------------ **/
@GetMapping(value = "/captcha/number/question")
public void getNumberCaptcha(HttpServletResponse response, HttpSession session)
throws IOException {
NumberCaptcha numberCaptcha = new NumberCaptcha();
OutputStream out = response.getOutputStream(); // 현재 화면에 뿌릴 outputStream
/** 이미지화할 글자 설정 **/
int imageWidth = 1000; // 문제 이미지 넓이
int imageHeight = 500; // 문제 이미지 높이
Color color = new Color(255, 0, 0); // 글자 색
Font font = new Font("TimeRoman", Font.PLAIN, 100); // 이미지 글체
ImageVo stringImage = new ImageVo(imageWidth, imageHeight, color, font);
/** 문자를 그릴 종이 만들기 **/
BufferedImage image = numberCaptcha.getWhitePaper(stringImage);
/** 문제 받아오기 **/
String question = numberCaptcha.getQuestion(session);
/** 종이(image)에 문제(questino)을 그리기 **/
numberCaptcha.draw(image, stringImage, question);
/** 이미지화된 글자의 content-type 설정 **/
response.setContentType("image/png");
/** 만든 이미지를 out(화면)에 png로 생성 **/
ImageIO.write(image, "png", out);
}
/** ------------ Number CaptCha Confrim (정답 확인) ------------ **/
@GetMapping(value = "/captcha/number/answer")
public @ResponseBody Map<Object, Object> confirmNumberCaptcha(HttpServletRequest request, HttpSession session) {
Map<Object, Object> result = new HashMap<>();
/** 정답 가져오기 **/
session = request.getSession();
String answer = (String) session.getAttribute("answer"); // 정답
String userAnswer = request.getParameter("userAnswer"); // 유저 정답
/** 정답 확인 **/
if (userAnswer.equals(answer)) result.put("response", "right");
else result.put("response", "wrong");
return result;
}
}
Controller
package com.lsj.study.Util;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import javax.servlet.http.HttpSession;
import com.lsj.study.Vo.ImageVo;
public class NumberCaptcha {
/**
* @param customImage : 글자 그릴 종이 설정
* @return : 글자 그릴 종이
*/
public BufferedImage getWhitePaper(ImageVo customImage) {
// 글자를 그릴 종이 크기 설정
int width = customImage.getImageWidth();
int height = customImage.getImageHeight();
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
return image;
}
/**
* @param session : 사용자 session
* @return : 랜덤 숫자 더하기 문제 생성
*/
public String getQuestion(HttpSession session) {
// 1번째 2번째 랜덤 숫자
int firstNum = (int) (Math.random() * 10 + 1);
int secondNum = (int) (Math.random() * 10 + 1);
// session에 답 저장
int answer = firstNum + secondNum;
session.setAttribute("answer", String.valueOf(answer));
System.out.println("Number-answer : " + answer);
// 문제 만들기
String question = String.valueOf(firstNum) + " + " + String.valueOf(secondNum) + " = ?";
return question;
}
/**
* @param image : 그림 그릴 종이
* @param imageVo : 그림 그릴 종이 설정
* @param question : 그림에 그릴 문제
*/
public void draw(BufferedImage image, ImageVo imageVo, String question) {
// 글자 이미지가 생성될 위치 선정
int left = 240;
int top = 300;
// Graphics2D 와 BufferedImage는 연동 된 느낌?
Graphics2D graphics = image.createGraphics();
// 글자색과 글자체 받아오기
graphics.setColor(imageVo.getImageColor());
graphics.setFont(imageVo.getFont());
// 해당 내용으로 그리기 작업
graphics.drawString(question, left, top);
graphics.dispose();
}
}
Util
package com.lsj.study.Vo;
import java.awt.Color;
import java.awt.Font;
public class ImageVo {
private int imageWidth;
private int imageHeight;
private Color imageColor;
private Font font;
public ImageVo(int imageWidth, int imageHeight, Color imageColor, Font font) {
this.imageWidth = imageWidth;
this.imageHeight = imageHeight;
this.imageColor = imageColor;
this.font = font;
}
public int getImageWidth() {
return imageWidth;
}
public void setImageWidth(int imageWidth) {
this.imageWidth = imageWidth;
}
public int getImageHeight() {
return imageHeight;
}
public void setImageHeight(int imageHeight) {
this.imageHeight = imageHeight;
}
public Color getImageColor() {
return imageColor;
}
public void setImageColor(Color imageColor) {
this.imageColor = imageColor;
}
public Font getFont() {
return font;
}
public void setFont(Font font) {
this.font = font;
}
}
package com.lsj.study.controller;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.lsj.study.Util.ColorCaptcha;
@Controller
public class CaptchaController {
/** ------------ Color CaptCha Page ------------ **/
@GetMapping(value = "/captcha/color")
public String captchaColor(HttpServletRequest request) {
/** Referer 필요시 사용 **/
//String referer = request.getHeader("Referer");
//if (referer.equals(rightReferer)) return "captcha/captcha_color";
//else return "captcha/wrong_page";
return "captcha/captcha_color";
}
/** ------------ Color CaptCha Question (문제의 공) ------------ **/
@GetMapping(value = "/captcha/color/question")
public void colorQuestion(HttpServletResponse response, HttpSession session) throws IOException {
/** 기존에 남아있는 정답 번호 삭제 **/
session.removeAttribute("colorAnswer");
ColorCaptcha colorCaptcha = new ColorCaptcha();
BufferedImage questionImage = colorCaptcha.drawQuestionColorBall(600, 200, session);
OutputStream out = response.getOutputStream(); // response outputStream
/** 이미지화된 글자의 content-type 설정 **/
response.setContentType("image/png");
/** 만든 이미지를 out(화면)에 png로 생성 **/
ImageIO.write(questionImage, "png", out);
}
/** ------------ Color CaptCha Multiple Choice (선택할 공) ------------ **/
@GetMapping(value = "/captcha/color/multiple/choice")
public void getMultipleChoice(HttpServletResponse response,HttpSession session) throws IOException {
ColorCaptcha colorCaptcha = new ColorCaptcha();
BufferedImage wrongImage1 = colorCaptcha.drawRandomColorBall(100, 100, session);
BufferedImage wrongImage2 = colorCaptcha.drawRandomColorBall(100, 100, session);
BufferedImage wrongImage3 = colorCaptcha.drawRandomColorBall(100, 100, session);
BufferedImage answerImage = colorCaptcha.drawAnswerColorBall(100, 100, session);
OutputStream out = response.getOutputStream(); // 현재 화면에 뿌릴 outputStream
/** 객관식 정보 담기 **/
Map<String, BufferedImage> multipleChoiceMap = new HashMap<String, BufferedImage>();
multipleChoiceMap.put("wrong1", wrongImage1);
multipleChoiceMap.put("wrong2", wrongImage2);
multipleChoiceMap.put("wrong3", wrongImage3);
multipleChoiceMap.put("answer", answerImage);
/** 객관식 번호 섞기 **/
List<String> keys = new ArrayList(multipleChoiceMap.keySet());
Collections.shuffle(keys);
/** 객관식 번호 부여하기 **/
BufferedImage questionNum1 = colorCaptcha.drawNumberOnColorBall("1");
BufferedImage questionNum2 = colorCaptcha.drawNumberOnColorBall("2");
BufferedImage questionNum3 = colorCaptcha.drawNumberOnColorBall("3");
BufferedImage questionNum4 = colorCaptcha.drawNumberOnColorBall("4");
/** key로 정답을 찾아 session 영역에 저장 **/
int answerNum = keys.indexOf("answer");
session.setAttribute("colorAnswer", answerNum + 1);
System.out.println("Color-Answer : " + (answerNum + 1) );
/** 객관식 리스트 합쳐 하나의 이미지로 재구성 **/
try {
/** 기본 바탕화면 구성 **/
BufferedImage mergedImage = new BufferedImage(600, 100, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = (Graphics2D) mergedImage.getGraphics();
graphics.setPaint(new Color(255, 255, 255));
graphics.fillRect(0, 0, mergedImage.getWidth(), mergedImage.getHeight());
/** 객관식 리스트 **/
graphics.drawImage(multipleChoiceMap.get(keys.get(0)), 50, 0, null);
graphics.drawImage(multipleChoiceMap.get(keys.get(1)), 200, 0, null);
graphics.drawImage(multipleChoiceMap.get(keys.get(2)), 350, 0, null);
graphics.drawImage(multipleChoiceMap.get(keys.get(3)), 500, 0, null);
/** 객관식 리스트의 번호 부여 **/
graphics.drawImage(questionNum1, 70, 0, null);
graphics.drawImage(questionNum2, 220, 0, null);
graphics.drawImage(questionNum3, 370, 0, null);
graphics.drawImage(questionNum4, 520, 0, null);
/** 화면에 출력 **/
response.setContentType("image/png");
ImageIO.write(mergedImage, "png", out);
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
/** ------------ Color CaptCha Confirm ------------ **/
@GetMapping(value = "/captcha/color/answer")
public @ResponseBody Map<Object, Object> confirmCaptChaColor
(HttpServletRequest request,HttpSession session) {
Map<Object, Object> result = new HashMap<>();
/** 정답 가져오기 **/
session = request.getSession();
String answer = String.valueOf((int) session.getAttribute("colorAnswer")); // 정답
String userAnswer = request.getParameter("userAnswer"); // 유저 정답
/** 정답 확인 **/
if (userAnswer.equals(answer)) result.put("response", "right");
else result.put("response", "wrong");
return result;
}
}
Controller
package com.lsj.study.Util;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import javax.servlet.http.HttpSession;
import com.lsj.study.Vo.ImageVo;
public class ColorCaptcha {
/**
* @param : width - 공 넓이
* height - 공 높이
* session - 사용자 Session
* @return : 문제로 낼 색칠된 공 이미지
*/
public BufferedImage drawQuestionColorBall(int width, int height, HttpSession session) {
session.removeAttribute("colorR");
session.removeAttribute("colorG");
session.removeAttribute("colorB");
BufferedImage questionImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = (Graphics2D) questionImage.getGraphics();
int colorR = (int) (Math.random() * 255);
int colorG = (int) (Math.random() * 255);
int colorB = (int) (Math.random() * 255);
session.setAttribute("colorR", colorR);
session.setAttribute("colorG", colorG);
session.setAttribute("colorB", colorB);
graphics.setColor(new Color(colorR, colorG, colorB));
int xPoint = 250;
int yPoint = 25;
int ovalWidth = 150;
int ovalHeight = 150;
graphics.fillOval(xPoint, yPoint, ovalWidth, ovalHeight); // 채우기 원
// graphics.drawOval(xPoint, yPoint, ovalWidth, ovalHeight); // 테두리 원
graphics.dispose();
return questionImage;
}
/**
*
* @param : width - 공 넓이
* height - 공 높이
* session - 사용자 Session
* @return : 랜덤으로 색칠된 오답인 공 이미지
*/
public BufferedImage drawRandomColorBall(int width, int height, HttpSession session) {
int answerR = 120;
if(session.getAttribute("colorR") != null)
answerR = (int) session.getAttribute("colorR");
BufferedImage questionImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = (Graphics2D) questionImage.getGraphics();
/** 정답과 중복 안되게 만들기 **/
int colorR;
if(answerR > 200) colorR = (int) (Math.random() * 150);
else if (answerR > 150) colorR = (int) (Math.random() * 100);
else if (answerR > 100) colorR = (int) (Math.random() * 100) + 50;
else if (answerR > 50) colorR = (int) (Math.random() * 100) + 100;
else colorR = (int) (Math.random() * 150) + 100;
int colorG = (int) (Math.random() * 255);
int colorB = (int) (Math.random() * 255);
graphics.setPaint(new Color(255, 255, 255));
graphics.setColor(new Color(colorR, colorG, colorB));
int xPoint = 0;
int yPoint = 0;
int ovalWidth = 100;
int ovalHeight = 100;
graphics.fillOval(xPoint, yPoint, ovalWidth, ovalHeight);
graphics.dispose();
return questionImage;
}
/**
* @param : width - 공 넓이
* height - 공 높이
* session - 사용자 Session
* @return : 정답인 색공 설정
*/
public BufferedImage drawAnswerColorBall(int width, int height, HttpSession session) {
int answerR = (int) session.getAttribute("colorR");
int answerG = (int) session.getAttribute("colorG");
int answerB = (int) session.getAttribute("colorB");
System.out.println("answerR : " + answerR);
System.out.println("answerG : " + answerG);
System.out.println("answerB : " + answerB);
BufferedImage questionImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = (Graphics2D) questionImage.getGraphics();
graphics.setPaint(new Color(255, 255, 255));
graphics.setColor(new Color(answerR, answerG, answerB));
int xPoint = 0;
int yPoint = 0;
int ovalWidth = 100;
int ovalHeight = 100;
graphics.fillOval(xPoint, yPoint, ovalWidth, ovalHeight);
graphics.dispose();
return questionImage;
}
/**
* @param : NumberToDraw - 공에 그릴 숫자
* @return : 오답 및 정답 색깔 공에 객관식 선택 번호 그리기
*/
public BufferedImage drawNumberOnColorBall(String NumberToDraw) {
/** 이미지화할 글자 설정 **/
int imageWidth = 550; // 문제 이미지 넓이
int imageHeight = 100; // 문제 이미지 높이
Color color = new Color(0, 0, 0); // 글자 색
Font font = new Font("TimeRoman", Font.PLAIN, 25); // 이미지 글체
ImageVo stringImage = new ImageVo(imageWidth, imageHeight, color, font);
/** 문자를 그릴 종이 만들기 **/
BufferedImage image = getWhitePaper(stringImage);
/** 문제 받아오기 **/
String question = NumberToDraw;
/** 글자 이미지가 생성될 위치 선정 **/
Graphics2D graphics2 = image.createGraphics(); // Graphics2D 와 BufferedImage는 연동 된 느낌?
/** 글자색과 글자체 받아오기 **/
graphics2.setColor(stringImage.getImageColor());
graphics2.setFont(stringImage.getFont());
/** 해당 내용으로 그리기 작업 **/
graphics2.drawString(question, 0, 40);
graphics2.dispose();
return image;
}
/**
* @param customImage : 그림 그릴 종이 크기 설정
* @return : 그림 그릴 종이
*/
public BufferedImage getWhitePaper(ImageVo customImage) {
// 글자를 그릴 종이 크기 설정
int width = customImage.getImageWidth();
int height = customImage.getImageHeight();
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
return image;
}
}
Color Ball Class
package com.lsj.study.Vo;
import java.awt.Color;
import java.awt.Font;
public class ImageVo {
private int imageWidth;
private int imageHeight;
private Color imageColor;
private Font font;
public ImageVo(int imageWidth, int imageHeight, Color imageColor, Font font) {
this.imageWidth = imageWidth;
this.imageHeight = imageHeight;
this.imageColor = imageColor;
this.font = font;
}
public int getImageWidth() {
return imageWidth;
}
public void setImageWidth(int imageWidth) {
this.imageWidth = imageWidth;
}
public int getImageHeight() {
return imageHeight;
}
public void setImageHeight(int imageHeight) {
this.imageHeight = imageHeight;
}
public Color getImageColor() {
return imageColor;
}
public void setImageColor(Color imageColor) {
this.imageColor = imageColor;
}
public Font getFont() {
return font;
}
public void setFont(Font font) {
this.font = font;
}
}