[java] LinkedHashMap에 대한 데이터 set, get 예제 총정리 맵 유틸 등
✅ LinkedHashMap<String, Object>
예제
import java.util.LinkedHashMap;
import java.util.Map;
public class LinkedHashMapExample {
public static void main(String[] args) {
// LinkedHashMap 선언
LinkedHashMap<String, Object> map = new LinkedHashMap<>();
// 값 저장 (set)
map.put("name", "홍길동");
map.put("age", 30);
map.put("isMember", true);
map.put("height", 175.5);
// 값 조회 (get)
String name = (String) map.get("name");
int age = (int) map.get("age");
boolean isMember = (boolean) map.get("isMember");
double height = (double) map.get("height");
// 출력
System.out.println("이름: " + name);
System.out.println("나이: " + age);
System.out.println("회원 여부: " + isMember);
System.out.println("키: " + height);
// 전체 순회
System.out.println("\n== 전체 출력 ==");
for (Map.Entry<String, Object> entry : map.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}
주요 포인트
LinkedHashMap
은 입력 순서 유지.Object
타입이기 때문에 get할 때는 형변환 필요.Map.Entry
를 활용하면 전체를 순회하면서 key/value 출력 가능.
안전한 꺼내기: null
체크 + 타입 체크
Object obj = map.get("age");
if (obj instanceof Integer) {
int age = (Integer) obj;
System.out.println("나이: " + age);
} else {
System.out.println("나이가 올바르게 저장되어 있지 않음");
}
✅ null
체크만 필요한 경우
if (map.containsKey("name")) {
String name = (String) map.get("name");
System.out.println("이름: " + name);
} else {
System.out.println("이름 정보 없음");
}
✅ 안전한 get 메서드 유틸리티 (예시)
실무에서는 이런 식의 유틸리티 메서드를 만들어두면 깔끔하게 처리할 수 있어요:
public class MapUtils {
public static String getString(Map<String, Object> map, String key) {
Object value = map.get(key);
return value instanceof String ? (String) value : null;
}
public static Integer getInt(Map<String, Object> map, String key) {
Object value = map.get(key);
return value instanceof Integer ? (Integer) value : null;
}
public static Boolean getBoolean(Map<String, Object> map, String key) {
Object value = map.get(key);
return value instanceof Boolean ? (Boolean) value : null;
}
// 필요한 타입들 추가 가능
}
사용 예시:
String name = MapUtils.getString(map, "name");
Integer age = MapUtils.getInt(map, "age");
if (name != null) {
System.out.println("이름: " + name);
}
🔐 보너스: Optional 사용 예시 (Java 8+)
Optional.ofNullable(map.get("age"))
.filter(val -> val instanceof Integer)
.map(val -> (Integer) val)
.ifPresent(age -> System.out.println("나이: " + age));
LinkedHashMap<String, Object>
용 안전한 유틸리티 클래스 (완성형)
import java.util.Map;
public class MapUtils {
public static String getString(Map<String, Object> map, String key) {
Object value = map.get(key);
return (value instanceof String) ? (String) value : null;
}
public static Integer getInt(Map<String, Object> map, String key) {
Object value = map.get(key);
return (value instanceof Integer) ? (Integer) value : null;
}
public static Long getLong(Map<String, Object> map, String key) {
Object value = map.get(key);
return (value instanceof Long) ? (Long) value : null;
}
public static Boolean getBoolean(Map<String, Object> map, String key) {
Object value = map.get(key);
return (value instanceof Boolean) ? (Boolean) value : null;
}
public static Double getDouble(Map<String, Object> map, String key) {
Object value = map.get(key);
return (value instanceof Double) ? (Double) value : null;
}
public static Object getOrDefault(Map<String, Object> map, String key, Object defaultValue) {
return map.getOrDefault(key, defaultValue);
}
public static boolean containsNonNull(Map<String, Object> map, String key) {
return map.containsKey(key) && map.get(key) != null;
}
}
사용 예:
LinkedHashMap<String, Object> map = new LinkedHashMap<>();
map.put("name", "홍길동");
map.put("age", 25);
String name = MapUtils.getString(map, "name");
Integer age = MapUtils.getInt(map, "age");
Jackson으로 Map<String, Object>
→ 객체 매핑
Jackson 라이브러리는 Map
을 객체로 손쉽게 변환할 수 있게 도와줍니다.
2-1. POJO 클래스 예시
public class User {
private String name;
private int age;
private boolean isMember;
// getter, setter
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public boolean isMember() { return isMember; }
public void setMember(boolean member) { isMember = member; }
}
2-2. Map → 객체 변환 코드
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.LinkedHashMap;
public class MapToObjectExample {
public static void main(String[] args) throws Exception {
LinkedHashMap<String, Object> map = new LinkedHashMap<>();
map.put("name", "홍길동");
map.put("age", 30);
map.put("member", true); // 필드명 주의: isMember → member
ObjectMapper mapper = new ObjectMapper();
User user = mapper.convertValue(map, User.class);
System.out.println(user.getName()); // 홍길동
System.out.println(user.getAge()); // 30
System.out.println(user.isMember()); // true
}
}
⚠️ 필드명이 맞아야 함 (
member
→isMember
는 setter 이름 따라감). 필요시@JsonProperty
사용 가능.
📦 Jackson 라이브러리 설치 (Maven)
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.3</version> <!-- 최신 버전 확인 -->
</dependency>
✅ 1. Map → JSON → 객체
변환
Jackson은 중간에 JSON 문자열을 거쳐서 객체로 변환할 수도 있습니다.
예제
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.*;
public class MapToJsonToObject {
public static void main(String[] args) throws Exception {
Map<String, Object> map = new HashMap<>();
map.put("name", "홍길동");
map.put("age", 30);
ObjectMapper mapper = new ObjectMapper();
// Map → JSON 문자열
String jsonString = mapper.writeValueAsString(map);
System.out.println("JSON: " + jsonString);
// JSON 문자열 → 객체
User user = mapper.readValue(jsonString, User.class);
System.out.println("이름: " + user.getName());
System.out.println("나이: " + user.getAge());
}
}
User
클래스는 앞선 예시와 동일
✅ 2. 중첩된 구조 (Deep Object Mapping)
예시 JSON 구조:
{
"name": "홍길동",
"age": 30,
"address": {
"city": "서울",
"zip": "12345"
}
}
2-1. POJO 클래스 정의
public class Address {
private String city;
private String zip;
// getters and setters
public String getCity() { return city; }
public void setCity(String city) { this.city = city; }
public String getZip() { return zip; }
public void setZip(String zip) { this.zip = zip; }
}
public class User {
private String name;
private int age;
private Address address;
// getters and setters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public Address getAddress() { return address; }
public void setAddress(Address address) { this.address = address; }
}
2-2. Map → 객체 매핑 (중첩 포함)
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.HashMap;
import java.util.Map;
public class DeepMapToObject {
public static void main(String[] args) throws Exception {
Map<String, Object> map = new HashMap<>();
map.put("name", "홍길동");
map.put("age", 30);
Map<String, Object> addressMap = new HashMap<>();
addressMap.put("city", "서울");
addressMap.put("zip", "12345");
map.put("address", addressMap);
ObjectMapper mapper = new ObjectMapper();
User user = mapper.convertValue(map, User.class);
System.out.println("이름: " + user.getName());
System.out.println("도시: " + user.getAddress().getCity());
}
}
✅ 3. Map/List 섞인 복합 구조 처리도 가능
{
"user": {
"name": "홍길동",
"roles": ["admin", "user"]
}
}
이 경우도 List<String>
으로 필드를 만들고 처리 가능합니다.
📌 보너스: JSON 필드명이 Java 필드명과 다를 때
import com.fasterxml.jackson.annotation.JsonProperty;
public class User {
@JsonProperty("full_name")
private String name;
// ...
}
객체 → JSON 문자열
ObjectMapper mapper = new ObjectMapper();
User user = new User();
user.setName("홍길동");
user.setAge(30);
String json = mapper.writeValueAsString(user);
System.out.println(json);
출력:
{“name”:”홍길동”,”age”:30}
✅ 2. 객체 → Map 변환
Map<String, Object> map = mapper.convertValue(user, new TypeReference<Map<String, Object>>() {});
System.out.println(map);
출력:
{name=홍길동, age=30}
배열/컬렉션 구조 매핑
예시 JSON:
[ {"name": "홍길동", "age": 30}, {"name": "이순신", "age": 45} ]
Java 코드
String jsonArray = "[{\"name\":\"홍길동\",\"age\":30},{\"name\":\"이순신\",\"age\":45}]";
List<User> userList = mapper.readValue(jsonArray, new TypeReference<List<User>>() {});
for (User u : userList) {
System.out.println(u.getName() + ", 나이: " + u.getAge());
}
JsonNode (트리 기반 접근)
JsonNode
는 동적 구조나 필드 존재 여부 등을 확인할 때 유용합니다.
예제 JSON
{
"user": {
"name": "홍길동",
"age": 30
},
"active": true
}
Java 코드
String json = "{\"user\":{\"name\":\"홍길동\",\"age\":30},\"active\":true}";
JsonNode root = mapper.readTree(json);
String name = root.path("user").path("name").asText();
int age = root.path("user").path("age").asInt();
boolean active = root.path("active").asBoolean();
System.out.println("이름: " + name);
System.out.println("나이: " + age);
System.out.println("활성: " + active);
path()
는 존재하지 않는 키를 접근해도 null 에러를 던지지 않음get()
은null
을 반환할 수 있으므로 주의
✅ 요약
기능 | 메서드 |
---|---|
객체 → JSON | writeValueAsString() |
객체 → Map | convertValue() |
JSON 배열 → List | readValue(..., TypeReference<>) |
JSON 트리 접근 | readTree() + JsonNode |
Jackson을 활용한 고급 기능들
- ✅
JsonNode
를 활용한 필터링/재구성 - ✅ 고급 직렬화 옵션:
@JsonView
,@JsonIgnore
,@JsonInclude
를 순서대로 예제 중심으로 정리해드릴게요.
✅ 1. JsonNode
로 JSON 필터링/재구성
📌 예제 JSON
{
"id": 1,
"name": "홍길동",
"password": "secret",
"roles": ["admin", "user"]
}
필터링: 특정 필드 제거 후 재구성
String json = "{\"id\":1,\"name\":\"홍길동\",\"password\":\"secret\",\"roles\":[\"admin\",\"user\"]}";
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(json);
// 필드 제거
((ObjectNode) root).remove("password");
// JSON 재생성
String filteredJson = mapper.writeValueAsString(root);
System.out.println(filteredJson);
출력:
{"id":1,"name":"홍길동","roles":["admin","user"]}
특정 필드만 추출해서 새 JSON 만들기
ObjectNode filtered = mapper.createObjectNode();
filtered.set("name", root.get("name"));
filtered.set("roles", root.get("roles"));
String newJson = mapper.writeValueAsString(filtered);
System.out.println(newJson);
✅ 2. @JsonIgnore
– 특정 필드 직렬화 제외
public class User {
private String name;
@JsonIgnore
private String password;
// getters and setters
}
User user = new User();
user.setName("홍길동");
user.setPassword("secret");
System.out.println(mapper.writeValueAsString(user));
출력:
{“name”:”홍길동”}
✅ 3. @JsonInclude
– null 값/기본값 제외 설정
@JsonInclude(JsonInclude.Include.NON_NULL)
public class User {
private String name;
private String email; // null 일 수 있음
// getters and setters
}
null인 필드는 출력되지 않음
다른 옵션들
옵션 | 설명 |
---|---|
NON_NULL | null 제외 |
NON_EMPTY | 빈 컬렉션, 빈 문자열 제외 |
NON_DEFAULT | 기본값 제외 (int = 0 등) |
ALWAYS | 항상 출력 (기본값) |
✅ 4. @JsonView
– 조건에 따라 필드 선택 출력
뷰 클래스 정의
public class Views {
public static class Public {}
public static class Internal extends Public {}
}
모델 클래스 적용
public class User {
@JsonView(Views.Public.class)
private String name;
@JsonView(Views.Internal.class)
private String password;
// getters and setters
}
직렬화 사용
User user = new User();
user.setName("홍길동");
user.setPassword("secret");
// Public view → password 제외
String jsonPublic = mapper
.writerWithView(Views.Public.class)
.writeValueAsString(user);
// Internal view → password 포함
String jsonInternal = mapper
.writerWithView(Views.Internal.class)
.writeValueAsString(user);
@JsonView
는 API 응답에서 역할별 권한 필드 출력 등에 유용합니다.
요약
기능 | 설명 |
---|---|
JsonNode | JSON을 트리처럼 동적으로 다루기 |
@JsonIgnore | 특정 필드 직렬화/역직렬화 제외 |
@JsonInclude | null/빈값 기본값 포함 여부 설정 |
@JsonView | 특정 상황(view)에 따라 필드 다르게 출력 |
조건부 필드 출력 설정 – @JsonView
활용 API 응답 DTO 구성
🔹 DTO 구성
public class Views {
public static class Public {}
public static class Admin extends Public {}
}
public class UserDto {
@JsonView(Views.Public.class)
private String name;
@JsonView(Views.Public.class)
private String email;
@JsonView(Views.Admin.class)
private String ssn;
// getters and setters
}
🔹 컨트롤러 응답 예시
@GetMapping("/user/public")
@JsonView(Views.Public.class)
public UserDto getPublicUser() {
return userService.getUserDto();
}
@GetMapping("/user/admin")
@JsonView(Views.Admin.class)
public UserDto getAdminUser() {
return userService.getUserDto();
}
Public
API에서는ssn
제외,Admin
API에서는 포함.
✅ 2. 보안 마스킹 전략 – 필드 단위 마스킹 커스텀
🔹 마스킹 어노테이션 정의
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Mask {
MaskType type() default MaskType.NAME;
}
public enum MaskType {
NAME, EMAIL, PHONE
}
🔹 Jackson Serializer 구현
public class MaskingSerializer extends JsonSerializer<String> {
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
Mask mask = (Mask) serializers.getAttribute("mask");
if (mask != null && value != null) {
switch (mask.type()) {
case NAME:
value = value.charAt(0) + "*".repeat(value.length() - 1);
break;
case EMAIL:
value = value.replaceAll("(?<=.).(?=.*@)", "*");
break;
case PHONE:
value = value.replaceAll(".(?=.{4})", "*");
break;
}
}
gen.writeString(value);
}
}
🔹 DTO에 적용
public class SecureUserDto {
private String name;
@Mask(type = MaskType.EMAIL)
private String email;
// getters and setters
}
🔹 Jackson 설정 시 컨텍스트 주입 필요 (ObjectMapper 커스터마이징)
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(String.class, new MaskingSerializer());
mapper.registerModule(module);
✅ 3. JsonNode 기반 필드 자동 필터링
시나리오: 민감 정보 필드를 리스트로 지정해 자동 제거
🔹 유틸리티 메서드
public static JsonNode filterSensitiveFields(JsonNode node, List<String> sensitiveKeys) {
if (node.isObject()) {
ObjectNode obj = (ObjectNode) node;
for (String key : sensitiveKeys) {
obj.remove(key);
}
for (Iterator<Map.Entry<String, JsonNode>> it = obj.fields(); it.hasNext(); ) {
Map.Entry<String, JsonNode> entry = it.next();
filterSensitiveFields(entry.getValue(), sensitiveKeys);
}
} else if (node.isArray()) {
for (JsonNode item : node) {
filterSensitiveFields(item, sensitiveKeys);
}
}
return node;
}
🔹 사용 예시
String json = "{\"name\":\"홍길동\",\"ssn\":\"123456-1234567\",\"email\":\"hong@example.com\"}";
JsonNode root = mapper.readTree(json);
List<String> sensitiveFields = List.of("ssn");
JsonNode filtered = filterSensitiveFields(root, sensitiveFields);
System.out.println(mapper.writeValueAsString(filtered));
🔒 실전에서 조합 전략 예시
@JsonView
로 역할 기반 API 필드 제어@Mask
로 필드 개별 마스킹- JsonNode 유틸로 동적 마스킹 or 필터링
💡 보안 + 직렬화 설계 팁
목적 | 전략 |
---|---|
API 응답 권한 제어 | @JsonView or DTO 분리 |
마스킹 처리 | @Mask + 커스텀 Serializer |
동적 필터링 | JsonNode 트리 순회로 키 삭제 |
예외 발생 방지 | Optional , @JsonInclude , null-safe 접근 |
Jackson의 고급 기능을 활용한 보안 마스킹 전략
✅ 마스킹 전략 고급 적용법
- AOP 기반 마스킹 적용
- Jackson MixIn
- Jackson
PropertyFilter
+BeanPropertyWriter
✅ 1. AOP 기반 마스킹 전략
📌 목적:
- DTO를 가공하지 않고, 컨트롤러 리턴 시 공통 마스킹 적용
- 로그/응답 전처리에 적합
🔹 AOP 마스킹 예시 (응답 전처리)
@Aspect
@Component
public class ResponseMaskingAspect {
@Around("@annotation(org.springframework.web.bind.annotation.GetMapping) || @annotation(org.springframework.web.bind.annotation.PostMapping)")
public Object maskResponse(ProceedingJoinPoint joinPoint) throws Throwable {
Object result = joinPoint.proceed();
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(result);
JsonNode root = mapper.readTree(json);
List<String> sensitiveFields = List.of("ssn", "password");
maskJsonNode(root, sensitiveFields);
return mapper.treeToValue(root, Object.class);
}
private void maskJsonNode(JsonNode node, List<String> fields) {
if (node.isObject()) {
ObjectNode obj = (ObjectNode) node;
for (String key : fields) {
if (obj.has(key)) obj.put(key, "****");
}
obj.fields().forEachRemaining(entry -> maskJsonNode(entry.getValue(), fields));
} else if (node.isArray()) {
for (JsonNode item : node) {
maskJsonNode(item, fields);
}
}
}
}
@RestControllerAdvice
와 조합하면, 모든 응답에 자동 적용도 가능
✅ 2. Jackson MixIn
– 외부 클래스에 @JsonIgnore
/@JsonProperty
등 적용
📌 목적:
- 소스 수정 없이 라이브러리 클래스 직렬화 제어
🔹 원본 클래스 (수정 불가)
public class ExternalUser {
public String username;
public String password;
}
🔹 MixIn 정의
public abstract class UserMixIn {
@JsonIgnore
public String password;
}
🔹 적용
ObjectMapper mapper = new ObjectMapper();
mapper.addMixIn(ExternalUser.class, UserMixIn.class);
String json = mapper.writeValueAsString(new ExternalUser("kim", "secret"));
// 출력: {"username":"kim"}
매우 유용한 기능 – 외부 DTO 응답 수정 없이 필터링 가능
✅ 3. PropertyFilter
+ BeanPropertyWriter
– 동적 마스킹 전략
📌 목적:
- 동적 조건(예: 사용자 권한)에 따라 필드를 숨기거나 마스킹
🔹 필터 정의
public class MaskingPropertyFilter extends SimpleBeanPropertyFilter {
private final Set<String> sensitiveFields;
public MaskingPropertyFilter(Set<String> sensitiveFields) {
this.sensitiveFields = sensitiveFields;
}
@Override
public void serializeAsField(Object bean, JsonGenerator gen, SerializerProvider prov, PropertyWriter writer) throws Exception {
if (sensitiveFields.contains(writer.getName())) {
gen.writeStringField(writer.getName(), "****");
} else {
writer.serializeAsField(bean, gen, prov);
}
}
}
🔹 적용 예시
FilterProvider filters = new SimpleFilterProvider()
.addFilter("maskFilter", new MaskingPropertyFilter(Set.of("ssn", "password")));
ObjectMapper mapper = new ObjectMapper();
mapper.setFilterProvider(filters);
UserDto user = new UserDto("홍길동", "123456-1234567", "hong@example.com");
String json = mapper
.writerWithDefaultPrettyPrinter()
.writeValueAsString(user);
System.out.println(json);
DTO 클래스에 반드시
@JsonFilter("maskFilter")
필요
@JsonFilter("maskFilter")
public class UserDto {
private String name;
private String ssn;
private String email;
}
🧠 언제 어떤 전략을 써야 하나요?
상황 | 추천 방식 |
---|---|
DTO 직접 수정 가능 | @JsonIgnore , @JsonInclude , @JsonView |
외부 클래스 직렬화 제어 필요 | MixIn 사용 |
필드를 조건부로 마스킹/숨김 | PropertyFilter or AOP |
JSON 전체에 유연한 트리 처리 필요 | JsonNode 유틸리티 |
응답 전처리/보안 필터 일괄 적용 | AOP + JsonNode 조합 |
💬 추가 가능 항목
- 요청/응답 로그 마스킹 (logback custom encoder)
- 마스킹 대상 필드 자동 탐지 (예:
@Sensitive
) - 동적 보안 정책 연동 (예: 사용자 등급별 마스킹 필드 다르게)
실전 프로젝트 기반 설정 예시나, 스프링 부트에서 필드 단위 보안 로깅 처리