몽구스에서 문서를 어떻게 업데이트 / 업데이트합니까?


367

아마도 그것은 아마도 시간 일 것입니다. 아마도 스파 스 문서에 빠져 익사하고 몽구스의 업데이트 개념에 대해 머리를 감쌀 수 없습니다 :)

거래는 다음과 같습니다.

연락처 스키마 및 모델이 있습니다 (속성 단축).

var mongoose = require('mongoose'),
    Schema = mongoose.Schema;

var mongooseTypes = require("mongoose-types"),
    useTimestamps = mongooseTypes.useTimestamps;


var ContactSchema = new Schema({
    phone: {
        type: String,
        index: {
            unique: true,
            dropDups: true
        }
    },
    status: {
        type: String,
        lowercase: true,
        trim: true,
        default: 'on'
    }
});
ContactSchema.plugin(useTimestamps);
var Contact = mongoose.model('Contact', ContactSchema);

필요한 필드가 포함 된 클라이언트로부터 요청을 받고 모델을 다음과 같이 사용합니다.

mongoose.connect(connectionString);
var contact = new Contact({
    phone: request.phone,
    status: request.status
});

이제 우리는 문제에 도달합니다.

  1. 내가 전화하면 contact.save(function(err){...}) 같은 전화 번호를 가진 연락처가 이미 존재하는 경우 오류가 발생합니다 (예상대로-고유)
  2. 나는 전화 할 수 없다 update()해당 방법이 문서에 없기 때문에 연락
  3. 모델에서 업데이트를 호출하면 :
    Contact.update({phone:request.phone}, contact, {upsert: true}, function(err{...})
    하면 몽구스 업데이트 구현에서 객체를 두 번째 매개 변수로 원하지 않기 때문에 어떤 종류의 무한 루프에 빠지게됩니다.
  4. 내가 똑같이하지만 두 번째 매개 변수에서 요청 속성의 연관 배열을 전달하면 {status: request.status, phone: request.phone ...}작동하지만 특정 연락처에 대한 참조가 없으며 해당 속성 createdAtupdatedAt속성을 찾을 수 없습니다 .

결론적으로, 내가 시도한 모든 결론 : document가 주어지면 문서를 contact업데이트하거나 그렇지 않으면 어떻게 추가합니까?

시간 내 줘서 고마워.


무엇에 접선에 대한 pre위해 save?
Shamoon

답변:


427

Mongoose는 이제 findOneAndUpdate를 통해이를 기본적으로 지원합니다 (MongoDB findAndModify 호출 ).

upsert = true 옵션은 객체가 존재하지 않는 경우 객체를 만듭니다. 기본값은 false 입니다.

var query = {'username': req.user.username};
req.newData.username = req.user.username;

MyModel.findOneAndUpdate(query, req.newData, {upsert: true}, function(err, doc) {
    if (err) return res.send(500, {error: err});
    return res.send('Succesfully saved.');
});

이전 버전에서 Mongoose는이 방법으로 이러한 후크를 지원하지 않습니다.

  • 기본값
  • 세터
  • 유효성 검사기
  • 미들웨어

17
이것은 최신 답변이어야합니다. 대부분의 사람들은 두 번의 호출을 사용하거나 (내 생각에) 네이티브 mongodb 드라이버로 넘어갑니다.
huggie

10
findOneAndUpdate의 문제점은 사전 저장이 실행되지 않는다는 것입니다.
a77icu5

2
Mongoose 또는 MongoDB의 버그처럼 들립니까?
Pascalius

8
문서에서 : "... findAndModify 도우미를 사용할 때는 다음이 적용되지 않습니다 : 기본값, 설정자, 유효성 검사기, 미들웨어" mongoosejs.com/docs/api.html#model_Model.findOneAndUpdate
kellen

2
@JamieHutber 이것은 기본적으로 설정되어 있지 않으며, 사용자 정의 속성입니다.
Pascalius

194

나는 같은 문제를 해결하기 위해 3 시간 동안 단단하게 태웠다. 특히, 전체 문서가있는 경우 "대체"하거나 그렇지 않으면 삽입하려고했습니다. 해결책은 다음과 같습니다.

var contact = new Contact({
  phone: request.phone,
  status: request.status
});

// Convert the Model instance to a simple object using Model's 'toObject' function
// to prevent weirdness like infinite looping...
var upsertData = contact.toObject();

// Delete the _id property, otherwise Mongo will return a "Mod on _id not allowed" error
delete upsertData._id;

// Do the upsert, which works like this: If no Contact document exists with 
// _id = contact.id, then create a new doc using upsertData.
// Otherwise, update the existing doc with upsertData
Contact.update({_id: contact.id}, upsertData, {upsert: true}, function(err{...});

Mongoose 프로젝트 페이지 에서 이에 대한 정보를 문서에 추가하도록 요청 하는 문제를 만들었습니다 .


1
현재 문서가 좋지 않은 것 같습니다. .는 API 문서의 일부 (이이처럼 보이는 페이지의 "업데이트"로 검색됩니다 : MyModel.update({ age: { $gt: 18 } }, { oldEnough: true }, fn);MyModel.update({ name: 'Tobi' }, { ferret: true }, { multi: true }, fn);
CpILL

사례 문서를 찾을 수없는 경우 어떤 _id가 사용됩니까? 몽구스는 그것을 생성 했습니까?
Haider

91

당신은 가까이했다

Contact.update({phone:request.phone}, contact, {upsert: true}, function(err){...})

그러나 두 번째 매개 변수는 수정 연산자가있는 객체 여야합니다.

Contact.update({phone:request.phone}, {$set: { phone: request.phone }}, {upsert: true}, function(err){...})

15
난 당신이 {$set: ... }내 독서 자동 양식으로 여기 에 부분 이 필요하다고 생각하지 않습니다
CpILL

5
그래, 몽구스는 $ 집합으로 모든 회전 말한다
grantwparks

1
이것은 글을 쓰는 시점에서 유효했습니다. MongoDB를 더 이상 사용하지 않으므로 최근 몇 달 동안 변화에 대해 이야기 할 수 없습니다. : D
chrixian

5
그래도 때때로 네이티브 드라이버를 사용하려는 경우 $ set을 사용하지 않는 것은 나쁜 습관이 될 수 있습니다.
UpTheCreek

당신은 삽입의 경우 세트 특정 필드에 $ 세트 $ setOnInsert을 사용할 수 있습니다
justin.m.chase

73

글쎄, 나는 오래 기다렸고 대답이 없었다. 마지막으로 전체 업데이트 / 업로드 접근 방식을 포기하고 다음을 수행했습니다.

ContactSchema.findOne({phone: request.phone}, function(err, contact) {
    if(!err) {
        if(!contact) {
            contact = new ContactSchema();
            contact.phone = request.phone;
        }
        contact.status = request.status;
        contact.save(function(err) {
            if(!err) {
                console.log("contact " + contact.phone + " created at " + contact.createdAt + " updated at " + contact.updatedAt);
            }
            else {
                console.log("Error: could not save contact " + contact.phone);
            }
        });
    }
});

작동합니까? 네. 나는 이것으로 행복합니까? 아마 아닙니다. 하나 대신 2 개의 DB 호출.
바라건대 미래의 몽구스 구현에는 Model.upsert기능 이 생길 것입니다 .


2
이 예제는 MongoDB 2.2에 추가 된 인터페이스를 사용하여 문서 형식으로 multi 및 upsert 옵션을 지정합니다. .. include :: /includes/fact-upsert-multi-options.rst 설명서에이 내용이 나와 있습니다. 여기서 어디로 가야할지 모르겠습니다.
Donald Derek

1
이것이 작동하지만, 이제 1 (업 사트) 만 필요할 때 2 개의 작업 (찾기, 업데이트)을 실행하고 있습니다. @chrixian은 이것을 수행하는 올바른 방법을 보여줍니다.
respectTheCode

12
Mongoose의 유효성 검사기가 시작할 수있는 유일한 대답이라는 점은 주목할 가치가 있습니다. docs에 따르면 업데이트를 호출하면 유효성 검사가 수행되지 않습니다.
Tom Spencer

당신 같은 @fiznool의 외모 수동 옵션에 전달할 수 있습니다 runValidators: true업데이트하는 동안 : 업데이트 워드 프로세서 (단, 업데이트 검사기는 실행 $set$unset운영)
대니

.upsert()모든 모델에서 사용할 수 있어야 하는 경우이 답변을 바탕으로 내 답변을 참조하십시오 . stackoverflow.com/a/50208331/1586406
spondbob

24

약속 체인을 사용하여 달성 할 수있는 매우 우아한 솔루션 :

app.put('url', (req, res) => {

    const modelId = req.body.model_id;
    const newName = req.body.name;

    MyModel.findById(modelId).then((model) => {
        return Object.assign(model, {name: newName});
    }).then((model) => {
        return model.save();
    }).then((updatedModel) => {
        res.json({
            msg: 'model updated',
            updatedModel
        });
    }).catch((err) => {
        res.send(err);
    });
});

왜 이것이 공표되지 않았습니까? 훌륭한 솔루션 인 것 같습니다.
MadOgre

훌륭한 솔루션으로 실제로 약속에 어떻게 접근하는지 다시 생각하게 만들었습니다.
lux

4
더욱 우아한는 재 작성하는 것 (model) => { return model.save(); }같은 model => model.save(), 또한 (err) => { res.send(err); }같은 err => res.send(err))
제레미 Thille

1
더 자세한 정보는 어디서 얻을 수 있습니까?
V0LT3RR4

17

저는 몽구스의 관리자입니다. 문서를 upsert하는 더 현대적인 방법은 Model.updateOne()함수 를 사용하는 입니다.

await Contact.updateOne({
    phone: request.phone
}, { status: request.status }, { upsert: true });

혐의 된 의사가 필요한 경우 Model.findOneAndUpdate()

const doc = await Contact.findOneAndUpdate({
    phone: request.phone
}, { status: request.status }, { upsert: true });

중요한 점은 filter매개 변수 의 고유 속성 을 updateOne()또는에 findOneAndUpdate(), 다른 속성을 매개 변수 에 넣어야한다는 것 update입니다.

다음 은 Mongoose로 문서업데이트 하는 방법에 대한 자습서입니다 .


15

이 질문에 대답하기 위해 StackOverflow 계정 JUST을 만들었습니다. 과일없이 웹을 검색 한 후 나는 방금 뭔가를 썼습니다. 이것이 내가 그렇게 한 방법이므로 모든 몽구스 모델에 적용 할 수 있습니다. 이 함수를 가져 오거나 업데이트중인 코드에 직접 추가하십시오.

function upsertObject (src, dest) {

  function recursiveFunc (src, dest) {
    _.forOwn(src, function (value, key) {
      if(_.isObject(value) && _.keys(value).length !== 0) {
        dest[key] = dest[key] || {};
        recursiveFunc(src[key], dest[key])
      } else if (_.isArray(src) && !_.isObject(src[key])) {
          dest.set(key, value);
      } else {
        dest[key] = value;
      }
    });
  }

  recursiveFunc(src, dest);

  return dest;
}

그런 다음 몽구스 문서를 업데이트하려면 다음을 수행하십시오.

YourModel.upsert = function (id, newData, callBack) {
  this.findById(id, function (err, oldData) {
    if(err) {
      callBack(err);
    } else {
      upsertObject(newData, oldData).save(callBack);
    }
  });
};

이 솔루션에는 2 회의 DB 호출이 필요할 수 있지만 다음과 같은 이점이 있습니다.

  • .save ()를 사용하고 있으므로 모델에 대한 스키마 유효성 검사
  • 업데이트 호출에서 수동 열거없이 깊게 중첩 된 객체를 업 사트 할 수 있으므로 모델 변경시 코드 업데이트에 대해 걱정할 필요가 없습니다.

소스에 기존 값이 있더라도 대상 객체는 항상 소스를 재정의합니다.

또한 배열의 경우 기존 개체가 대체하는 것보다 긴 배열을 갖는 경우 이전 배열의 끝에있는 값이 유지됩니다. 전체 배열을 upsert하는 쉬운 방법은 이전 배열을 upsert 전에 빈 배열로 설정하는 것입니다.

업데이트-2016 년 1 월 16 일 기본 값 배열이있는 경우 추가 조건을 추가했지만 몽구스는 "set"기능을 사용하지 않고 배열이 업데이트되는 것을 인식하지 못합니다.


2
이것을 위해 acc를 만들기위한 +1 : P .save ()를 사용하기 위해 또 다른 + 1을 줄 수 있기를 바랍니다. findOneAndUpate () 때문에 유효성 검사기와 사전, 게시 등을 사용할 수 없습니다. 감사합니다, 이것도 확인하겠습니다
user1576978

죄송합니다,하지만 여기에 작동하지 않았다 :( 내가 호출 스택의 크기를 초과있어
user1576978

어떤 버전의 lodash를 사용하고 있습니까? lodash 버전 2.4.1을 사용하고 있습니다. 감사합니다!
Aaron Mast

또한, 얼마나 복잡한 객체를 고집하고 있습니까? 이들이 너무 크면 노드 프로세스가 오브젝트를 병합하는 데 필요한 재귀 호출 수를 처리하지 못할 수 있습니다.
Aaron Mast

이것을 사용했지만 if(_.isObject(value) && _.keys(value).length !== 0) {스택 오버플로를 중지하려면 보호 조건을 추가해야했습니다 . Lodash 4 이상에서는 keys통화 에서 비 객체 값을 객체로 변환하는 것처럼 보이 므로 재귀 가드는 항상 참이었습니다. 더 좋은 방법이 있을지 모르지만 지금은 거의 효과가 있습니다.
Richard G

12

문서를 하나의 컬렉션으로 업데이트 / 업데이트해야했습니다. 내가 한 일은 다음과 같이 새 객체 리터럴을 만드는 것입니다.

notificationObject = {
    user_id: user.user_id,
    feed: {
        feed_id: feed.feed_id,
        channel_id: feed.channel_id,
        feed_title: ''
    }
};

데이터베이스의 다른 곳에서 얻은 데이터로 구성된 다음 모델에서 업데이트를 호출합니다.

Notification.update(notificationObject, notificationObject, {upsert: true}, function(err, num, n){
    if(err){
        throw err;
    }
    console.log(num, n);
});

이것은 스크립트를 처음 실행 한 후에 얻는 출력입니다.

1 { updatedExisting: false,
    upserted: 5289267a861b659b6a00c638,
    n: 1,
    connectionId: 11,
    err: null,
    ok: 1 }

그리고 이것은 스크립트를 두 번째로 실행할 때의 결과입니다.

1 { updatedExisting: true, n: 1, connectionId: 18, err: null, ok: 1 }

몽구스 버전 3.6.16을 사용하고 있습니다.


10
app.put('url', function(req, res) {

        // use our bear model to find the bear we want
        Bear.findById(req.params.bear_id, function(err, bear) {

            if (err)
                res.send(err);

            bear.name = req.body.name;  // update the bears info

            // save the bear
            bear.save(function(err) {
                if (err)
                    res.send(err);

                res.json({ message: 'Bear updated!' });
            });

        });
    });

몽구스에서 업데이트 방법을 해결하는 더 좋은 방법은 다음과 같습니다 . 자세한 내용은 Scotch.io 를 확인 하십시오. 이것은 확실히 나를 위해 일했다! !!


5
이것이 MongoDB의 업데이트와 같은 일을한다고 생각하는 것은 실수입니다. 원자가 아닙니다.
Valentin Waeselynck

1
@ValentinWaeselynck 답변을 백업하고 싶습니다. Scotch의 코드는 깨끗하지만 문서를 가져 와서 업데이트하고 있습니다. 이 과정의 중간에 문서가 변경 될 수 있습니다.
Nick Pineda

8

2.6에서 소개 된 버그가 있으며 2.7에도 영향을줍니다.

2.4에서 올바르게 작동하는 데 사용되는 upsert

https://groups.google.com/forum/#!topic/mongodb-user/UcKvx4p4hnY https://jira.mongodb.org/browse/SERVER-13843

살펴보면 중요한 정보가 들어 있습니다.

업데이트 :

upsert가 작동하지 않는다는 의미는 아닙니다. 사용 방법에 대한 좋은 예는 다음과 같습니다.

User.findByIdAndUpdate(userId, {online: true, $setOnInsert: {username: username, friends: []}}, {upsert: true})
    .populate('friends')
    .exec(function (err, user) {
        if (err) throw err;
        console.log(user);

        // Emit load event

        socket.emit('load', user);
    });

7

당신은 단순히 이것으로 레코드를 업데이트하고 응답으로 업데이트 된 데이터를 얻을 수 있습니다

router.patch('/:id', (req, res, next) => {
    const id = req.params.id;
    Product.findByIdAndUpdate(id, req.body, {
            new: true
        },
        function(err, model) {
            if (!err) {
                res.status(201).json({
                    data: model
                });
            } else {
                res.status(500).json({
                    message: "not found any relative data"
                })
            }
        });
});

6

이것은 나를 위해 일했습니다.

app.put('/student/:id', (req, res) => {
    Student.findByIdAndUpdate(req.params.id, req.body, (err, user) => {
        if (err) {
            return res
                .status(500)
                .send({error: "unsuccessful"})
        };
        res.send({success: "success"});
    });

});


감사. 이것은 마침내 나를 위해 일한 사람이었습니다!
Luis Febro

4

다음은 미들웨어 및 유효성 검증기를 호출하면서 작성 / 업데이트하는 가장 간단한 방법입니다.

Contact.findOne({ phone: request.phone }, (err, doc) => {
    const contact = (doc) ? doc.set(request) : new Contact(request);

    contact.save((saveErr, savedContact) => {
        if (saveErr) throw saveErr;
        console.log(savedContact);
    });
})

3

여기에 도착하는 사람이라면 여전히 후크 지원 기능이있는 "업 세팅"을위한 솔루션을 찾고 있습니다. 이것이 제가 테스트하고 작업 한 것입니다. 여전히 2 개의 DB 호출이 필요하지만 단일 호출에서 시도한 것보다 훨씬 안정적입니다.

// Create or update a Person by unique email.
// @param person - a new or existing Person
function savePerson(person, done) {
  var fieldsToUpdate = ['name', 'phone', 'address'];

  Person.findOne({
    email: person.email
  }, function(err, toUpdate) {
    if (err) {
      done(err);
    }

    if (toUpdate) {
      // Mongoose object have extra properties, we can either omit those props
      // or specify which ones we want to update.  I chose to update the ones I know exist
      // to avoid breaking things if Mongoose objects change in the future.
      _.merge(toUpdate, _.pick(person, fieldsToUpdate));
    } else {      
      toUpdate = person;
    }

    toUpdate.save(function(err, updated, numberAffected) {
      if (err) {
        done(err);
      }

      done(null, updated, numberAffected);
    });
  });
}

3

발전기를 사용할 수 있다면 더욱 쉬워집니다.

var query = {'username':this.req.user.username};
this.req.newData.username = this.req.user.username;
this.body = yield MyModel.findOneAndUpdate(query, this.req.newData).exec();

3

다른 해결책은 나를 위해 일하지 않았습니다. 게시 요청을 사용하고 데이터를 삽입하면 데이터를 업데이트하고 _id도 제거 해야하는 요청 본문과 함께 전송됩니다.

router.post('/user/createOrUpdate', function(req,res){
    var request_data = req.body;
    var userModel = new User(request_data);
    var upsertData = userModel.toObject();
    delete upsertData._id;

    var currentUserId;
    if (request_data._id || request_data._id !== '') {
        currentUserId = new mongoose.mongo.ObjectId(request_data._id);
    } else {
        currentUserId = new mongoose.mongo.ObjectId();
    }

    User.update({_id: currentUserId}, upsertData, {upsert: true},
        function (err) {
            if (err) throw err;
        }
    );
    res.redirect('/home');

});

2
//Here is my code to it... work like ninj

router.param('contractor', function(req, res, next, id) {
  var query = Contractors.findById(id);

  query.exec(function (err, contractor){
    if (err) { return next(err); }
    if (!contractor) { return next(new Error("can't find contractor")); }

    req.contractor = contractor;
    return next();
  });
});

router.get('/contractors/:contractor/save', function(req, res, next) {

    contractor = req.contractor ;
    contractor.update({'_id':contractor._id},{upsert: true},function(err,contractor){
       if(err){ 
            res.json(err);
            return next(); 
            }
    return res.json(contractor); 
  });
});


--

2
User.findByIdAndUpdate(req.param('userId'), req.body, (err, user) => {
    if(err) return res.json(err);

    res.json({ success: true });
});

이 코드 스 니펫은 문제를 해결할 수 있지만 질문에 왜 또는 어떻게 대답하는지는 설명하지 않습니다. 제발 코드에 대한 설명을 포함 하는 정말 귀하의 게시물의 품질을 개선하는 데 도움으로. 앞으로 독자에게 질문에 대한 답변을 제공하고 있으며 해당 사람들이 귀하의 코드 제안 이유를 모를 수도 있습니다. 신고자 / 검토 자 : 이 답변과 같은 코드 전용 답변의 경우 공감하지 마십시오.
Patrick

2

다음 기술 가이 여행 이미 최고, 우리는 플러그인을 작성하고 우리가 그것을 초기화하면 그 있도록 몽구스에 첨부 할 수의 대답, .upsert()모든 모델에 사용할 수 있습니다.

plugins.js

export default (schema, options) => {
  schema.statics.upsert = async function(query, data) {
    let record = await this.findOne(query)
    if (!record) {
      record = new this(data)
    } else {
      Object.keys(data).forEach(k => {
        record[k] = data[k]
      })
    }
    return await record.save()
  }
}

db.js

import mongoose from 'mongoose'

import Plugins from './plugins'

mongoose.connect({ ... })
mongoose.plugin(Plugins)

export default mongoose

그럼 당신은 뭔가를 할 수 있습니다 User.upsert({ _id: 1 }, { foo: 'bar' })또는 YouModel.upsert({ bar: 'foo' }, { value: 1 })당신이 원하는 때마다.


2

방금이 문제로 돌아와서 Aaron Mast의 답변에 따라 플러그인을 게시하기로 결정했습니다.

https://www.npmjs.com/package/mongoose-recursive-upsert

몽구스 플러그인으로 사용하십시오. 전달 된 객체를 재귀 적으로 병합하는 정적 메소드를 설정합니다.

Model.upsert({unique: 'value'}, updateObject});

0

이 커피 스크립트는 Node와 함께 작동합니다. 트릭은 클라이언트에서 보내고 반환 할 때 _id가 ObjectID 래퍼를 제거하므로 업데이트를 위해 교체해야합니다 (_id가 제공되지 않은 경우 저장은 삽입 및 추가로 되돌아갑니다) 하나).

app.post '/new', (req, res) ->
    # post data becomes .query
    data = req.query
    coll = db.collection 'restos'
    data._id = ObjectID(data._id) if data._id

    coll.save data, {safe:true}, (err, result) ->
        console.log("error: "+err) if err
        return res.send 500, err if err

        console.log(result)
        return res.send 200, JSON.stringify result

0

Martin Kuzdowicz가 위에 게시 한 내용을 기반으로합니다. 몽구스와 json 객체의 깊은 병합을 사용하여 업데이트를 수행하려면 다음을 사용하십시오. mongoose의 model.save () 함수와 함께 mongoose는 json의 다른 값에 의존하는 전체 검증을 수행 할 수 있습니다. deepmerge 패키지 https://www.npmjs.com/package/deepmerge 가 필요합니다 . 그러나 이것은 매우 가벼운 패키지입니다.

var merge = require('deepmerge');

app.put('url', (req, res) => {

    const modelId = req.body.model_id;

    MyModel.findById(modelId).then((model) => {
        return Object.assign(model, merge(model.toObject(), req.body));
    }).then((model) => {
        return model.save();
    }).then((updatedModel) => {
        res.json({
            msg: 'model updated',
            updatedModel
        });
    }).catch((err) => {
        res.send(err);
    });
});

1
NoSQL 주입을req.body 테스트하기 전에있는 그대로 사용하지 않도록 주의 해야합니다 ( owasp.org/index.php/Testing_for_NoSQL_injection 참조 ).
Traveling Tech Guy

1
@TravelingTechGuy 여전히 Node와 Mongoose를 처음 사용한다는 것에 감사드립니다. 유효성 검사기를 사용하는 몽구스 모델이 주입 시도를 잡기에 충분하지 않습니까? model.save () 동안
Chris

-5

위의 게시물을 읽은 후이 코드를 사용하기로 결정했습니다.

    itemModel.findOne({'pid':obj.pid},function(e,r){
        if(r!=null)
        {
             itemModel.update({'pid':obj.pid},obj,{upsert:true},cb);
        }
        else
        {
            var item=new itemModel(obj);
            item.save(cb);
        }
    });

r이 null이면 새 항목을 만듭니다. 그렇지 않으면 업데이트시 새 항목이 생성되지 않으므로 upsert를 업데이트에 사용하십시오.


그것이 몽고에 대한 두 번의 전화라면 실제로 그렇게 귀찮지 않습니까?
huggie
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.